automatic 5 trap firing system for our Clay Pigeon Club
rhcpc
Posts: 7
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
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
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
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
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
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
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
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
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
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.
Do all really fire simultaneously, or does that order have to be randomized?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
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)
Sorry Tracy, this code never generates 11111 any ideas?
// 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?
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)?
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!!!"
it's then used as a FIVE byte array by this code.
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.
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.
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?