Shop OBEX P1 Docs P2 Docs Learn Events
automatic 5 trap firing system for our Clay Pigeon Club — Parallax Forums

automatic 5 trap firing system for our Clay Pigeon Club

rhcpcrhcpc Posts: 7
edited 2007-07-25 20:07 in BASIC Stamp
Hi I am new to the whole BASIC Stamp thing but I though I would give it a go to develop a automatic 5 trap firing system for our Clay Pigeon Club.

When program first runs it is in a "standby" mode awaiting the button push.

When button pushed the program jumps to the "main" code, firstly it generates a random number between 1 and 5 this is the number of traps to fire (so if rnd=3 we will be firing 3 of the 5 traps in the system)

Each cycle will be 250 clays so the next part checks if the latest no of traps to fire would take us over 250 and if so goes back and generates another until this would not be the case.

A running counter is kept so that when we have reached 250 the program returns to "standyby" mode.

The next thing that I need to do is get which traps to fire so I have an array firing_list(4) which I loop through n times where n is the number of traps to fire from above, and generate another random number between 1 and 5 and assign it to the array position.

When I then write out the value on my array I have a number of firing sets that total 250 and my program goes into "standby"

The problem is my firing sets can contain the same number, I need this not to be the case, the block of commented code was my attempt at solving this but it doesn't work.

Any suggestions?

'*********************************************************************************************
' {$STAMP BS2}
' {$PBASIC 2.5}
Btn············ PIN···· 0······ 'pin to be input
btnWrk········· VAR···· Byte
max_clays······ VAR···· Byte··· 'number of clays to fire
counter········ VAR···· Byte··· 'running count of sent clays
seed··········· VAR···· Word··· 'used by random generator
rnd············ VAR···· Byte··· 'used by random generator
rnd1··········· VAR···· Byte··· 'used by random generator
firing_list···· VAR···· Byte(4)
i·············· VAR···· Byte··· 'used by random generator
x·············· VAR···· Byte··· 'used by random generator

standy:
··· counter=0
··· 'seed=0···································· 'initialise to generate same set each time
··· max_clays = 250
··· HIGH 7
··· LOW 10
··· BUTTON Btn, 1, 255, 10, btnWrk, 0, reset
··· GOTO main
main:
··· LOW 7
··· HIGH 10
··· gen_num_traps:································· 'routine to generate random number between 1 and 5
····· seed=seed+1
····· IF seed >65535 THEN
······· seed=0
····· ENDIF
····· RANDOM seed
····· rnd=seed//5+1
··· IF counter + rnd > max_clays THEN·············· 'if newly generated rnd number would take count over var max_clays send back to gen_rnd_num:
····· GOTO gen_num_traps
··· ENDIF
··· counter=counter+rnd
··· 'DEBUG DEC rnd, CR
··· FOR i = 0 TO rnd-1
····· get_trap_no:
······· seed=seed+1
······· IF seed >65535 THEN
········· seed=0
······· ENDIF
······· RANDOM seed
······· rnd1=seed//5+1
····· 'need to see if new number has been generated previously in the firing set
····· 'if it has then we need to generate again without incrementing i
····· 'only when number not already generated should it be assigned to firing_list array.
····· 'FOR x = 0 TO rnd-1
······· 'IF firing_list(x) = rnd1 THEN
········· 'DEBUG DEC rnd1, " - DUP", CR
········· 'GOTO get_trap_no
······· 'ELSE
········· 'DEBUG DEC rnd1, " - OK", CR
········· 'firing_list(i) = rnd1
······· 'ENDIF
····· 'NEXT
····· firing_list(i) = rnd1
··· NEXT
··· '************************************************************************************************************************************
··· 'DEBUG ONLY - WRITE OUT FIRING SET
··· '************************************************************************************************************************************
··· FOR i = 0 TO rnd-1
····· DEBUG DEC firing_list(i), CR
··· NEXT
··· DEBUG "*******************", CR
··· '************************************************************************************************************************************
··· '************************************************************************************************************************************
··· IF counter = max_clays THEN standy············ 'end program if count = max_clays
··· PAUSE 100
··· BUTTON Btn, 1, 255, 10, btnWrk, 0, main
··· GOTO pause_loop
pause_loop:······································ 'suspend main program retain count value
··· LOW 10
··· HIGH 15
··· PAUSE 250
··· LOW 15
··· PAUSE 250
··· BUTTON Btn, 1, 255, 10, btnWrk, 0, pause_loop
··· GOTO main
'*********************************************************************************************

Thanks for looking

Post Edited By Moderator (Chris Savage (Parallax)) : 7/21/2007 9:50:47 PM GMT

Comments

  • Bruce BatesBruce Bates Posts: 3,045
    edited 2007-07-21 09:31
    rchpc -

    You really don't need to know if you are duplicating a number in your set. What you DO need to know if you have generated a sufficient number of random numbers. Since ZERO isn't valid, start the firing array with zeros in it, and clear it to zeros after every iteration. Then, as you're generating random numbers, scan through the table each time and continue generating random numbers until entires_used = desired_number. Let duplicates fall where they may. The array is small so the continued scanning shouldn't hurt the performance much.

    The unseen problem you're eventually going to have is that RANDOM doesn't create a truly random number, only a pseudo-random number. Thus, you will see the same patterns being generated each time. Not to worry though.

    You are requiring a user input, and that is the key. In lieu of using the BUTTON command, you need to retain contol yourself by fetching the button press yourself in a loop. Each time you DO NOT see the button press, you add one to a counter. Regardless of what that counter reads in the end, that's what you use for your first seed value. NOW you have introduced some real randomness in the initial RANDOM statement.

    Your looping input routine is just going to look something like this:

    Fetch_Button_Press:

    DO

    rand_initial = rand_initial + 1

    LOOP UNTIL Btn = 1

    RANDOM rand_initial

    ...

    Regards,

    Bruce Bates

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
  • Philip GamblinPhilip Gamblin Posts: 202
    edited 2007-07-21 15:03
    Maybe I'm thinking too much. They way I read this is 250 clays / 5 traps = 50 clays per trap. Since your generating ramdom numbers, looks to me that you need logic that keeps track of how mnay clays have been fired from each trap. You could conceiveably run out of clays at a station without knowing it. You need to keep track of not only how total clays but also how many are available at each station.

    To make life simpler, I'd suggest an alternative to the random number generator. Use timer/counter that seeds your trap selection. If you used a 200 sec counter/timer to feed trap selection you'd potentially have 200 trap sequences. You are limited by the number of combinations of 5 taken 2 at a time. You could also you vary the delay of the timimg of the firing. I still think you need to keep track of how many clays are left at each trap, when you run out at a trap it has to be removed from the selection process

    This is a cool project and totally within the capability of the Stamp. Interfacing to the firing mechanism could be a trick
  • rhcpcrhcpc Posts: 7
    edited 2007-07-21 19:40
    Thanks for the input, firstly Bruce the first random generator (it's random enough for this application) tells me how many different traps will fire and the second is supposed to dictate which ones fire. So if the first rnd comes back with say 3 then the second may for example return an array of 3 items for eg. 2,4 & 5.

    The array values within each firing order needs to be unique as the same trap cannot fire twice simultaniously and we need to be able to fire all 5 at once should the firing sequence dictate it.

    Looking back at my original code it must be reasonably simple to check if the newly generated number already exists in the array and if it does generate another to replace it without too much alteration?

    Once I have an array with the unique trap numbers in it I can then turn pins high and trigger the relevan trap via a transistor circuit attached to each trap.

    Could you explain why the following code from my original post does not work:

    ' {$STAMP BS2}
    ' {$PBASIC 2.5}
    Btn············ PIN···· 0······ 'pin to be input
    btnWrk········· VAR···· Byte
    max_clays······ VAR···· Byte··· 'number of clays to fire
    counter········ VAR···· Byte··· 'running count of sent clays
    seed··········· VAR···· Word··· 'used by random generator
    rnd············ VAR···· Byte··· 'used by random generator
    rnd1··········· VAR···· Byte··· 'used by random generator
    firing_list···· VAR···· Byte(4)
    i·············· VAR···· Byte··· 'used by random generator
    x·············· VAR···· Byte··· 'used by random generator

    standby:
    ··· counter=0
    ··· 'seed=0···································· 'initialise to generate same set each time
    ··· max_clays = 250
    ··· HIGH 7
    ··· LOW 10
    ··· BUTTON Btn, 1, 255, 10, btnWrk, 0, standby
    ··· GOTO main
    main:
    ··· LOW 7
    ··· HIGH 10
    ··· gen_num_traps:································· 'routine to generate random number between 1 and 5
    ····· seed=seed+1
    ····· IF seed >65535 THEN
    ······· seed=0
    ····· ENDIF
    ····· RANDOM seed
    ····· rnd=seed//5+1
    ··· IF counter + rnd > max_clays THEN·············· 'if newly generated rnd number would take count over var max_clays send back to gen_rnd_num:
    ····· GOTO gen_num_traps
    ··· ENDIF
    ··· counter=counter+rnd
    ··· 'DEBUG DEC rnd, CR
    ··· gen_trap_nos:
    ····· FOR i = 0 TO rnd-1
    ······· seed=seed+1
    ······· IF seed >65535 THEN
    ········· seed=0
    ······· ENDIF
    ······· RANDOM seed
    ······· rnd=seed//5+1
    ··· NEXT
    ··· FOR x = 0 TO rnd-1
    ····· IF firing_list(x) = rnd THEN
    ······· GOTO gen_trap_nos
    ····· ELSE
    ······· firing_list(i) = rnd1
    ····· ENDIF
    ··· NEXT

    ··· '************************************************************************************************************************************
    ··· 'DEBUG ONLY - WRITE OUT FIRING SET
    ··· '************************************************************************************************************************************
    ··· FOR i = 0 TO rnd-1
    ····· DEBUG DEC firing_list(i), CR
    ··· NEXT
    ··· DEBUG "*******************", CR
    ··· '************************************************************************************************************************************
    ··· '************************************************************************************************************************************
    ··· IF counter = max_clays THEN standby············ 'end program if count = max_clays
    ··· PAUSE 100
    ··· BUTTON Btn, 1, 255, 10, btnWrk, 0, main
    ··· GOTO pause_loop
    pause_loop:······································ 'suspend main program retain count value
    ··· LOW 10
    ··· HIGH 15
    ··· PAUSE 250
    ··· LOW 15
    ··· PAUSE 250
    ··· BUTTON Btn, 1, 255, 10, btnWrk, 0, pause_loop
    ··· GOTO main
    Now Philip, I hear what you're saying re running out of clays, I really ought to explain the application in a little more detail so as to explain why this is not a problem.

    This solution is to be used for a specific event where a five man team stand in a line and 250 clays are released over them in a driven fashion. Currently we have 5 people each manning a trap with the firing order being transmitted to them via a recorded firing sequence through headphones.

    Each trap can hold in excess of 250 clays each, what I'm hoping to achieve is 1 person can man all 5 traps and at the end of each sequence top up with clays should it be required.

    I like the idea of varying the firing timing, maybe I will work that into the code when I get the basic routine working.

    As far as interfacing with the firing mech I am happy I will be able to do this as described above once I have an array of trap numbers. LOL!

    Thanks for your help the whole electronics and Stamp thing is totally new to me but it has captured my imagination, having a real world application in mind makes the learning curve a whole lot more interesting

    Post Edited (rhcpc) : 7/21/2007 8:07:42 PM GMT
  • Bruce BatesBruce Bates Posts: 3,045
    edited 2007-07-21 21:44
    rhcpc -

    You asked about the following routine, so I made comments on the right-hand side:

    Could you explain why the following code from my original post does not work:

    gen_num_traps:
    seed=seed+1
    ' IF seed >65535 THEN '<-- This can never happen so
    ' seed=0 '<-- you may as well remove it,
    ' ENDIF '<-- along with this line.
    RANDOM seed
    rnd=seed//5+1 '<-- How is this affected and the FOR below if "seed" = 0?
    FOR x = 0 TO rnd-1 '<-- " " ...
    IF firing_list(x) = rnd THEN
    GOTO gen_num_traps
    ELSE
    firing_list(i) = rnd1 '<-- Are you sure you want to be using "i" as an index here?
    ENDIF '<-- Also, I would make "firing_list" the last variable in the program
    NEXT '<-- so that "i" and "x" which follow it can't get CLOBBERED by an
    ' '<-- out-of-range subscript.

    Those are the first things I see. Make those changes, and see what happens.

    Regards,

    Bruce Bates

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
  • Philip GamblinPhilip Gamblin Posts: 202
    edited 2007-07-22 04:40
    This will show the traps and whic ones to fire. You get to do the button stuff, I've never used it. I'm sure there is a better way to format the output, but it's readable. Regarding your program, I would suggest you work incrementally, get one piece working at a time, then put them together. Paste it in and see how it works. If it is ok to have all five traps fire, change the >=31 to>31
    Happy Shooting


    ' {$STAMP BS2}
    ' {$PBASIC 2.5}
    'this program generates and displays 5 pseudo random bits
    '
    NumOfTraps VAR Byte
    WhichTraps VAR Nib
    Seed VAR Word
    Rand VAR Word
    x VAR Word

    seed=42
    Main:
    RANDOM seed
    IF Seed.LOWBYTE >=31 THEN NumOfTraps = Seed.LOWBYTE >>2 'this shifts the low byte of seed Binary 11111=31
    NumOfTraps= Seed.LOWBYTE.LOWNIB ' two bits to the right and zero
    ' fills bits 7 and 6


    DEBUG "Fire the traps according to the table. You can fire on 1 or 0",CR
    DEBUG "Trap 1 Trap2 Trap3 Trap 4 Trap 5",CR
    DEBUG " ",BIN NumOfTraps.BIT0, " ",BIN NumOfTraps.BIT1,TAB," ",BIN NumOfTraps.BIT2,TAB," ",BIN NumOfTraps.BIT3,TAB," ",BIN NumOfTraps.BIT4
    PAUSE 5000
    DEBUG CLS
    GOTO Main
  • rhcpcrhcpc Posts: 7
    edited 2007-07-22 13:39
    Hi Phil

    Oh, yes I'm liking this solution one minor bug I thing BIT4 is never 1, if we can sort this I'm certain we're on a winner.
  • Philip GamblinPhilip Gamblin Posts: 202
    edited 2007-07-22 20:26
    If you change the ">=31" to =>63, that should do it.
  • rhcpcrhcpc Posts: 7
    edited 2007-07-23 08:20
    I tried that but still BIT4 is always 0
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2007-07-23 17:21
    This is similar to Phil's approach. Instead of generating a random number from 1 to 5 to specify how many and then a second series of random numbers for which ones, just generate a random number from 1 to 31 with 5 random bits:

    DO
      RANDOM seed
      rnd = seed // 31  'random number from %00000 to %11111
    LOOP UNTIL rnd   '  this disallows zero
    
    ' now you have five bits, with 1=yesFire, 0=notFire
    
    FOR i=0 to 4
      IF rnd.bit0(i) THEN firing_list(i) = i+1   ' this makes a list like   "50021" but %10011  conveys the same info. 
    NEXT
    



    Do all really fire simultaneously, or does that order have to be randomized?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • rhcpcrhcpc Posts: 7
    edited 2007-07-23 17:46
    I like this approach; generating a number betwen 1 and 31 then firing the corresponding trap if the bit is 1. As previously stated the Stamp is a new piece of kit for me so I appreciate peoples patience.

    It is likely that at some point all five do need to fire at once, I will have to scale this up to around 10 traps in the final solution (when the new traps are purchased).

    The code you supplied is working as expected, I will incorporate it into my main program and report back. Thanks for your help.

    Don't suppose you have any info regarding cost effective UHF Transmitters and Receivers suitable for PCB applications (just thinking ahead to my next project, LOL)
  • rhcpcrhcpc Posts: 7
    edited 2007-07-23 21:10
    ' {$STAMP BS2}
    ' {$PBASIC 2.5}
    
    Seed            VAR     Word
    rnd             VAR     Word
    i               VAR     Byte
    firing_count    VAR     Byte
    firing_list     VAR     Byte(4)
    DO
    seed=seed+1
    DO
      RANDOM seed
      rnd = seed // 31  'random number from %00000 to %11111
    LOOP UNTIL (rnd)   '  this disallows zero
    ' now you have five bits, with 1=yesFire, 0=notFire
    firing_count=0
    FOR i=0 TO 4
      IF rnd.BIT0(i) THEN firing_list(i) = i+1   ' this makes a list like   "50021" but %10011  conveys the same info.
      IF rnd.BIT0(i) THEN
         firing_count=firing_count+1
      ENDIF
    NEXT
    PAUSE 200
    DEBUG " ",BIN rnd.BIT0, TAB, " ",BIN rnd.BIT1,TAB," ",BIN rnd.BIT2,TAB," ",BIN rnd.BIT3,TAB," ",BIN rnd.BIT4, "                     ", DEC firing_count, CR, CR
    LOOP
    

    Sorry Tracy, this code never generates 11111 any ideas?
  • LawsonLawson Posts: 870
    edited 2007-07-23 21:48
    heh, "rnd = seed // 31" needs to be "rnd = seed // 32"
    // is the modulus operator, it returns the remainder of a division operation. %11111 = 31 so 31/31 exactly equals 1 so the remainder is 0. (window's built in calculator can do binary numbers. it is great for this stuff. put it into 'scientific' mode)

    hope this helps,
    Marty

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Lunch cures all problems! have you had lunch?
  • rhcpcrhcpc Posts: 7
    edited 2007-07-24 21:18
    ' {$STAMP BS2}
    ' {$PBASIC 2.5}
    Btn             PIN     0
    btnWrk          VAR     Byte
    max_clays       VAR     Byte
    Seed            VAR     Word
    rnd             VAR     Word
    i               VAR     Byte
    firing_batch    VAR     Byte
    firing_count    VAR     Byte
    firing_list     VAR     Byte(4)
    standby:
        firing_count=0                                    'initialise to generate same set each time
        max_clays = 250
        HIGH 13
        LOW 14
        BUTTON Btn, 1, 255, 10, btnWrk, 0, standby
        GOTO main
    main:
        DO
          LOW 13
          HIGH 14
          GOSUB getfiringbatch
          IF firing_count + firing_batch > max_clays THEN               'if newly generated rnd number would take count over var max_clays send back to gen_rnd_num:
            GOTO main
          ELSE
            firing_count=firing_count+firing_batch
          ENDIF
          GOSUB fire_traps
          IF firing_count = max_clays THEN standby             'end program if count = max_clays
          PAUSE 100
          BUTTON Btn, 1, 255, 10, btnWrk, 0, main
          GOTO pause_loop
        LOOP
    pause_loop:
        LOW 14
        HIGH 15
        PAUSE 250
        LOW 15
        PAUSE 250
        BUTTON Btn, 1, 255, 10, btnWrk, 0, pause_loop
        GOTO main
    getfiringbatch:
        firing_batch=0
        seed=seed+1
        DO
          RANDOM seed
          rnd = seed // 32
        LOOP UNTIL (rnd)
        FOR i=0 TO 4
          IF rnd.BIT0(i) THEN firing_list(i) = i+1
          IF rnd.BIT0(i) THEN
             firing_batch=firing_batch+1
          ENDIF
        NEXT
        RETURN
    fire_traps:
        IF rnd.BIT0 THEN HIGH 1
        IF rnd.BIT1 THEN HIGH 3
        IF rnd.BIT2 THEN HIGH 5
        IF rnd.BIT3 THEN HIGH 7
        IF rnd.BIT4 THEN HIGH 9
        PAUSE 1000
        LOW 1
        LOW 3
        LOW 5
        LOW 7
        LOW 9
        RETURN
    

    Thanks for all your help, I think I have a working model (at least on BOE), will now work on getting it on a PCB. Just another quick question I will need an IC carrier to mount my STAMP in prior to mounting on PCB, do I just need a std 24 pin carrier (are they a standard size)?
  • allanlane5allanlane5 Posts: 3,815
    edited 2007-07-24 22:47
    Yup, 24-pin IC sockets come in narrow-DIP and wide-DIP. You'll need the 0.6" 'wide-DIP' form factor, which is a standard size.
  • Shawn LoweShawn Lowe Posts: 635
    edited 2007-07-25 18:10
    Soemthing that might help you later is to format your program according to the elements of PBasic style. Instead of
    low 1
    low 3
    you could reformat to
    trap_1 = is_off
    trap_3 = is_off

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Shawn Lowe


    My last words shall be - "NOT YET!!!"
  • LawsonLawson Posts: 870
    edited 2007-07-25 20:07
    A bug you REALLY should be careful of in the future. Firing_list is defined as a four byte array by this code.

    firing_list     VAR     Byte(4)
    



    it's then used as a FIVE byte array by this code.

    FOR i=0 TO 4
          IF rnd.BIT0(i) THEN firing_list(i) = i+1
          IF rnd.BIT0(i) THEN
             firing_batch=firing_batch+1
          ENDIF
        NEXT
    



    Since PBasic has no array bounds checking the program compiles and runs quite happily. Also, since Firing_list is the last variable declared and you have no Nib or Bit variables the extra memory loaction accessed by the above code is unused, so no funny behavior is seen in this code. Define Firing_list as a five element array to squash this bug.

    firing_list     VAR     Byte(5)
    





    IF firing_count = max_clays THEN standby
    


    change "=" to ">=" to be sure the code stops at 250 clays. (not strictly needed, but more bug resistant)

    Also, readability is your friend! the PIN declaration makes programs vastly more readable. it also easier to update.

    Trap_1     PIN     1
    
    'then to manipulate the pin u can just use
    LOW Trap_1
    'and
    HIGH Trap_1
    



    you'd have to test this, but I also think doing a "goto main" to exit the DO LOOP structure in Main will cause problems. I'd recommend using an EXIT instead and then following the DO LOOP with a 'GOTO main' should avoid this. (also, the 'goto main' at the end of Pause_loop is redundant, simply make it a subroutine and enter it with a 'GOSUB' for the same effect) Why could all this matter? The program as coded now has a recursive structure in Main. Every time a 'goto main' is used to exit this DO LOOP the resources consumed by the DO LOOP are not freed up. Eventually the program should run out of memory and crash.

    Good luck on all this,
    Marty

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Lunch cures all problems! have you had lunch?
Sign In or Register to comment.