REM >Arachnid REM Functions to use with Arachnid boxes REM REM Contents: REM FNswitch_number(box%, line%, nboxes%) REM FNswitch_number_2(box%, line%, nboxes%) REM REM PROCsingle_pellet(line%) REM PROCfast_pellet(line%, numpellets%) REM PROCspaced_pellet(line%, numpellets%, timer%, gap%) :REM Unrestricted parameters REM PROCold_spaced_pellet(line%, numpellets%, timer%, gap%) :REM Restricted parameter form REM REM PROCstart_flash_line(line%, timer%, on%, off%) REM PROCstop_flash_line(line%, timer%) REM PROCold_start_flash_line(line%, timer%, on%, off%) REM REM PROCflash_line_number(line%, timer%, on%, off%, count) :REM Unrestricted. REM PROCold_flash_line_number(line%, timer%, on%, off%, count) :REM Restricted. REM REM PROCflash_line_time(line%, timer1%, timer2%, on%, off%, duration%) :REM Unrestricted. REM REM PROCuserfunc_start_flash(on_func$, off_func$, timer%, on%, off%) REM PROCuserfunc_stop_flash(off_func$, timer%) REM PROCuserfunc_flash_time(on_func$, off_func$, timer1%, timer2%, on%, off%, duration%) REM REM FNswitch_on_line(line%, bogus%) :REM 7 June 99. REM FNswitch_off_line(line%, bogus%) REM REM FNfood_mass(pellets%) REM FNliquid_volume(dips%) REM REM *** MULTIPLE PARAMETERS REM Possible, for example: REM PROCpipe_timer(2,200,0,"FNfive_params("+STR$(A)+","+STR$(B)+","+STR$(C)+","+STR$(DOG)+",",ECHIDNA,E%) REM Discovered (and then found in manual, page I-66) after these were written. REM They'd obviously allow a greater range of parameters, REM but these functions work. If it ain't broke... REM REM *** UPDATE: 4 Dec 98 REM Jill found the count=32 limit problematic in PROCflash_line_number. REM The "old_" functions use the single parameter type. REM New versions have been written for unrestricted parameters. REM ======================================================================== DEF FNswitch_number(box%, line%, nboxes%) REM ======================================================================== REM use if boxes are wired REM all houselights/all traylights/... = line%*nboxes% + box% - 1 REM ======================================================================== DEF FNswitch_number_2(box%, line%, nlines%) REM ======================================================================== REM use if boxes are wired REM all box1 wires/all box2 wires/... = (box%-1)*nlines% + line% REM ======================================================================== DEF PROCfast_pellet(line%, numpellets%) REM ======================================================================== REM Dispenses pellets as fast as poss. REM Empirically determined pulse times. REM REM The 'Arachnid' way would be to use timers. REM However, ND found bugs in Arachnid concerned with such timing REM and the 4cs/event delay imposed by a REPEAT...UNTIL mechanism REM doesn't seem to cause problems. LOCAL i%, t% IF numpellets%=0 THEN ENDPROC FOR i% = 1 TO numpellets% PROCsingle_pellet(line%) IF i% t%+4 REM don't bother waiting on the last go NEXT ENDPROC REM ======================================================================== DEF PROCspaced_pellet(line%, numpellets%, timer%, gap%) REM ======================================================================== REM Unrestricted parameter version. LOCAL dummy% IF numpellets%=0 THEN ENDPROC IF timer%>=256 OR line%>=256 THEN VDU7:PRINT"*** Error: invalid parameters to PROCspaced_pellet" ENDPROC ENDIF dummy%=FNspaced_pellet_2(line%, numpellets%, timer%, gap%, 1) ENDPROC DEF FNspaced_pellet_2(line%, numpellets%, timer%, gap%, R%) IF R%=0 =0 PROCsingle_pellet(line%) IF numpellets%=1 THEN =0 PROCpipe_timer(timer%, gap%, 0, "FNspaced_pellet_2("+STR$(line%)+","+STR$(numpellets%-1)+","+STR$(timer%)+",",gap%,E%) =0 REM ======================================================================== DEF PROCold_spaced_pellet(line%, numpellets%, timer%, gap%) REM ======================================================================== REM Dispenses pellets, with a gap between each pellet. REM Meant to make differences in number of pellets more REM discriminable. (Conditioned reinforcer, too...) IF numpellets%=0 THEN ENDPROC IF timer%>=256 OR gap%>=(256*8) OR numpellets%>16 OR line%>=256 THEN VDU7:PRINT"*** Error: invalid parameters to PROCold_spaced_pellet" ENDPROC ENDIF REM allow up to and including 16 pellets, 'cos it takes one off first. PROCsingle_pellet(line%) IF numpellets%=1 THEN ENDPROC REM What we have to do now is to send a few more pellets. REM The receiving function must know at least two of REM (a) timer%, (b) gap%, (c) pellets left to go. REM I choose to implement a recurrent timer, which eliminates REM the need for the receiving function to know gap%. REM Obviously other combinations are possible, but REM I think the best choice, because gap is not restricted to REM byte range while timer% is and I define numpellets% to be. REM (Can't see you wanting to dispense >255 pellets in one go!) REM (Fucking deficient event system, if I may say so.) REM So we have to combine timer% (range 0-255) and pellets_left. REM Can't omit gap%, because then you'd have to use a recurrent timer REM and you can't change their parameters as you go, so you can't REM encode changes in pellets-to-go. So have to set a new timer each REM time, so need to know gap%. REM Can't omit timer%, because either you use a recurrent timer REM and then have to kill it by number, or you re-pipe the timer REM and have to use its number then. REM Can't omit pellets-to-go, because you can't set a timer for a finite REM number of cycles and I don't want to use multiple timer numbers for REM a single pellet dispenser. REM And, by the way, need line%. REM Therefore have to encode all four. timer% is 0-255. line% is 0-255. REM BASIC only allows integers to be 256*256*256*128-1. REM So we're left with 256*128 for gap and numpellets. REM Either 20s/8 pellets, 10s/16 pellets, 5s/32 pellets... REM I choose 20s, 16 pellets. That's 256*8. REM Actually, 15 pellets. REM Recursive code. LOCAL parm% parm% = timer% + line%*256 + gap%*256*256 + (numpellets%-1)*256*256*256*8 PROCpipe_timer(timer%,gap%,0,"FNold_spaced_pellet_2(",parm%,R%) ENDPROC DEF FNold_spaced_pellet_2(parm%,R%) IF R%=0 =0 LOCAL timer%, numpellets%, gap%, line% timer% = parm% MOD 256 line% = (parm% DIV 256) MOD 256 gap% = (parm% DIV (256*256)) MOD (256*8) numpellets% = parm% DIV (256*256*256*8) PROCsingle_pellet(line%) IF numpellets%=1 THEN =0 parm% = timer% + line%*256 + gap%*256*256 + (numpellets%-1)*256*256*256*8 PROCpipe_timer(timer%,gap%,0,"FNold_spaced_pellet_2(",parm%,R%) =0 REM ======================================================================== DEF PROCsingle_pellet(line%) REM ======================================================================== REM plips out a pellet, doesn't wait afterwards REM Empirically determined pulse time. LOCAL t% t% = TIME PROCswitch_on(line%, E%) REPEAT UNTIL TIME > t% + 4 PROCswitch_off(line%, E%) ENDPROC REM ======================================================================== DEF PROCstart_flash_line(line%, timer%, on%, off%) REM ======================================================================== REM Unrestricted version. LOCAL dummy% IF line%>=256 OR timer%>=256 OR on%=0 OR off%=0 THEN VDU7:PRINT"Invalid parameters to PROCstart_flash_line" ENDPROC ENDIF dummy%=FNflash_line_2on(line%, timer%, on%, off%, 1) ENDPROC DEF FNflash_line_2off(line%, timer%, on%, off%, R%) IF R%=0 =0 PROCswitch_off(line%, E%) PROCpipe_timer(timer%, off%, 0, "FNflash_line_2on("+STR$(line%)+","+STR$(timer%)+","+STR$(on%)+",",off%,E%) =0 DEF FNflash_line_2on(line%, timer%, on%, off%, R%) IF R%=0 =0 PROCswitch_on(line%, E%) PROCpipe_timer(timer%, on%, 0, "FNflash_line_2off("+STR$(line%)+","+STR$(timer%)+","+STR$(on%)+",",off%,E%) =0 REM ======================================================================== DEF PROCold_start_flash_line(line%, timer%, on%, off%) REM ======================================================================== REM *** Use: sets the line flashing, using timer%. REM To stop, use PROCstop_flash_line with the same timer number. REM Typically, calling program would set a second timer for this. REM *** Restriction: although it accepts times in cs, this REM function has a temporal resolution of 10cs. REM This is to allow the passing of four parameters through Arachnid. REM *** Restriction: only allows pulse times of up to 1280cs, REM for the same reason on% = on% DIV 10 off% = off% DIV 10 IF line%>=256 OR on%>=128 OR off%>=128 OR timer%>=256 OR on%=0 OR off%=0 THEN VDU7:PRINT"Invalid parameters to PROCold_start_flash_line" ENDPROC ENDIF LOCAL parm% parm% = line% + timer%*256 + on%*256*256 + off%*256*256*128 PROCswitch_on(line%, E%) PROCpipe_timer(timer%, on%*10, 0, "FNold_flash_line_2off(", parm%, E%) ENDPROC DEF FNold_flash_line_2off(parm%, R%) IF R%=0 =0 LOCAL line%,timer%,on%,off% line% = parm% MOD 256 timer% = (parm% DIV 256) MOD 256 on% = (parm% DIV (256*256)) MOD 128 off% = parm% DIV (256*256*128) PROCswitch_off(line%,E%) PROCpipe_timer(timer%, off%*10, 0, "FNold_flash_line_2on(", parm%, E%) =0 DEF FNold_flash_line_2on(parm%, R%) IF R%=0 =0 LOCAL line%,timer%,on%,off% line% = parm% MOD 256 timer% = (parm% DIV 256) MOD 256 on% = (parm% DIV (256*256)) MOD 128 off% = parm% DIV (256*256*128) PROCswitch_on(line%,E%) PROCpipe_timer(timer%, on%*10, 0, "FNold_flash_line_2off(", parm%, E%) =0 REM ======================================================================== DEF PROCstop_flash_line(line%, timer%) REM ======================================================================== PROCkill_timer(timer%,E%) PROCswitch_off(line%,E%) ENDPROC REM ======================================================================== DEF PROCflash_line_number(line%, timer%, on%, off%, count%) REM ======================================================================== REM Unrestricted version. REM You can abort it cleanly using PROCstop_flash_line if necessary. LOCAL dummy% IF line%>=256 OR timer%>=256 OR on%=0 OR off%=0 OR count%=0 THEN VDU7:PRINT"*** Invalid parameters to PROCflash_line_number" ENDPROC ENDIF dummy%=FNflash_line_number_2on(line%, timer%, on%, off%, count%, 1) ENDPROC DEF FNflash_line_number_2on(line%, timer%, on%, off%, count%, R%) IF R%=0 =0 PROCswitch_on(line%, E%) REM counter decrement occurs here: PROCpipe_timer(timer%, on%, 0, "FNflash_line_number_2off("+STR$(line%)+","+STR$(timer%)+","+STR$(on%)+","+STR$(off%)+",",count%-1,E%) =0 DEF FNflash_line_number_2off(line%, timer%, on%, off%, count%, R%) IF R%=0 =0 PROCswitch_off(line%, E%) IF count%=0 =0 :REM endpoint PROCpipe_timer(timer%, off%, 0, "FNflash_line_number_2on("+STR$(line%)+","+STR$(timer%)+","+STR$(on%)+","+STR$(off%)+",",count%,E%) =0 REM ======================================================================== DEF PROCold_flash_line_number(line%, timer%, on%, off%, count%) REM ======================================================================== REM *** If you need to, can use PROCstop_flash_line to abort early. REM *** Restriction: although it accepts times in cs, this REM function has a temporal resolution of 10cs. REM This is to allow the passing of four parameters through Arachnid. REM Takes 8 bits for line, 8 for timer, leaves 15 for on/off/count. REM Count must be odd#, so 5 (up to 32) rather than 3 (up to 8). REM Thus 5 bits each for on/off (up to 32), so 3.2 sec max. on% = on% DIV 10 off% = off% DIV 10 IF line%>=256 OR timer%>=256 OR on%>=32 OR off%>=32 OR count%>32 OR count%=0 THEN VDU7:PRINT"*** Invalid parameters to PROCold_flash_line_number" ENDPROC ENDIF LOCAL parm% parm% = line% + timer%*256 + on%*256*256 + off%*256*256*32 + (count%-1)*256*256*32*32 PROCswitch_on(line%,E%) PROCpipe_timer(timer%, on%*10, 0, "FNold_flash_line_number_2off(", parm%, E%) ENDPROC DEF FNold_flash_line_number_2off(parm%, R%) IF R%=0 =0 LOCAL line%, timer%, count%, on%, off% line% = parm% MOD 256 timer% = (parm% DIV 256) MOD 256 on% = (parm% DIV (256*256)) MOD 32 off% = (parm% DIV (256*256*32)) MOD 32 count% = (parm% DIV (256*256*32*32)) PROCswitch_off(line%,E%) IF count%=0 =0 :REM that's the endpoint PROCpipe_timer(timer%, off%*10, 0, "FNold_flash_line_number_2on(", parm%, E%) =0 DEF FNold_flash_line_number_2on(parm%, R%) IF R%=0 =0 LOCAL line%, timer%, count%, on%, off% line% = parm% MOD 256 timer% = (parm% DIV 256) MOD 256 on% = (parm% DIV (256*256)) MOD 32 off% = (parm% DIV (256*256*32)) MOD 32 count% = (parm% DIV (256*256*32*32)) PROCswitch_on(line%,E%) parm% = line% + timer%*256 + on%*256*256 + off%*256*256*32 + (count%-1)*256*256*32*32 PROCpipe_timer(timer%, on%*10, 0, "FNold_flash_line_number_2off(", parm%, E%) =0 REM ======================================================================== DEF FNfood_mass(pellets%) REM ======================================================================== = pellets%*45/1000 REM a pellet is 45 mg REM ======================================================================== DEF FNliquid_volume(dips%) REM ======================================================================== = dips%*0.05 REM a cupful is 0.05 ml REM ======================================================================== DEF PROCflash_line_time(line%, timer1%, timer2%, on%, off%, duration%) REM ======================================================================== REM calls the other flashing functions to provide the final, most REM useful interface to the flashing code. REM Uses the unrestricted (multiple parameter) versions. IF timer1%=timer2% THEN VDU7:PRINT"*** PROCflash_line_time requires two unique timers" ENDPROC ENDIF REM Other error-checking performed by the sub-functions. PROCstart_flash_line(line%, timer1%, on%, off%) PROCpipe_timer(timer2%, duration%, 0, "FNflash_line_time2("+STR$(line%)+",",timer1%,E%) ENDPROC DEF FNflash_line_time2(line%, timer%, R%) IF R%=0 =0 PROCstop_flash_line(line%, timer%) =0 REM ======================================================================== DEF PROCuserfunc_start_flash(on_func$, off_func$, timer%, on%, off%) REM ======================================================================== REM Calls user_defined functions that take NO PARAMETER. REM MAY THROW RUNTIME ERRORS (uses eval) - USER TAKES RESPONSIBILITY REM FOR ENSURING FUNCTIONS ARE VALID AND WORKING. REM Example of use: REM PROCuserfunc_start_flash("FNgo("+STR$(box%)+")","FNstop("+STR$(box%)+")",3,400,200) REM ... REM DEF FNgo(box%):PROCswitch_on(houselight%(box%),E%):=0 REM DEF FNstop(box%):PROCswitch_off(houselight%(box%),E%):=0 LOCAL dummy% IF timer%<0 OR timer%>=256 OR on%=0 OR off%=0 THEN VDU7:PRINT"Invalid parameters to PROCuserfunc_start_flash" ENDPROC ENDIF dummy%=FNuserfunc_flash2on(on_func$, off_func$, timer%, on%, off%, 1) ENDPROC DEF FNuserfunc_flash2off(on_func$, off_func$, timer%, on%, off%, R%) IF R%=0 =0 LOCAL dummy% dummy% = EVAL(off_func$) PROCpipe_timer(timer%, off%, 0, "FNuserfunc_flash2on("""+on_func$+""","""+off_func$+""","+STR$(timer%)+","+STR$(on%)+",",off%,E%) REM N.B. Explicit quotes must be inserted. =0 DEF FNuserfunc_flash2on(on_func$, off_func$, timer%, on%, off%, R%) IF R%=0 =0 LOCAL dummy% dummy% = EVAL(on_func$) PROCpipe_timer(timer%, on%, 0, "FNuserfunc_flash2off("""+on_func$+""","""+off_func$+""","+STR$(timer%)+","+STR$(on%)+",",off%,E%) REM N.B. Quotes =0 REM ======================================================================== DEF PROCuserfunc_stop_flash(off_func$, timer%) REM ======================================================================== LOCAL dummy% PROCkill_timer(timer%,E%) dummy% = EVAL(off_func$) ENDPROC REM ======================================================================== DEF PROCuserfunc_flash_time(on_func$, off_func$, timer1%, timer2%, on%, off%, duration%) REM ======================================================================== REM SEE COMMENTS FOR PROCuserfunc_start_flash. Is used similarly. IF timer1%=timer2% THEN VDU7:PRINT"*** PROCuserfunc_flash_time requires two unique timers" ENDPROC ENDIF REM Other error-checking performed by the sub-functions. PROCuserfunc_start_flash(on_func$, off_func$, timer1%, on%, off%) PROCpipe_timer(timer2%, duration%, 0, "FNuserfunc_flash_time2("""+off_func$+""",",timer1%,E%) REM N.B. Quotes. ENDPROC DEF FNuserfunc_flash_time2(off_func$, timer%, R%) IF R%=0 =0 PROCuserfunc_stop_flash(off_func$, timer%) =0 REM ======================================================================== DEF FNswitch_on_line(line%, bogus%) REM ======================================================================== REM These two functions are convenient to use with timers REM for single events, e.g. REM PROCpipe_timer(t%, 100, 0, "FNswitch_on_line(", light%, E%) REM to switch on a light 100 cs from now. IF bogus%=0 =0 PROCswitch_on(line%, E%) =0 REM ======================================================================== DEF FNswitch_off_line(line%, bogus%) REM ======================================================================== IF bogus%=0 =0 PROCswitch_off(line%, E%) =0