'*******************************************************************
'**************** PICfocus telescope focuser V5.05 *****************
'*******************************************************************
'*																   *
'* A fine-control electronic telescope focuser          		   *
'* with wireless remote control.             					   *
'*                                                                 *
'* Features:                                                       *
'*  - wireless remote control (500 metre range)                    *
'*	- 8 preset locations stored in non-volatile memory             *
'*	- fast, slow, and single-step movement controls                *
'*	- autofocus capable with an astrocam and AstroSnap software    *
'*	- high-resolution, normal resolution, and hi-torque step modes *
'*	- power conservation option to power down motor coils after    *
'*	  3 seconds of inactivity (5 seconds for wireless mode)		   *
'*	- firmware upgradable with in-circuit serial programmer & PC   *
'*	  software	                                                   *
'*  - "aux" function via PC to control 2 output lines directly     *
'*    or 2 hobby servos (for control of other accessories          *
'*	  on PB5|PB6)                                   	           *
'*  - ASSISTfocus feature - use with FWHM focusing with a readout  *
'*    get "close" to focus, then move INWARD, take reading, then   *
'*    start the ASSISTfocus routine. At each pause, press OUT (up) *
'*    if focus improves, or IN (down) if focus is worse.           *
'*  - backlash compensation for geartrain backlash in the focuser  *
'*                                                                 *
'*******************************************************************
'
'
'Written for PIC 16F88-4MHz internal clock
'Designed for a 4-bit mux'd 8 momentary pushbutton inputs (+1 limit switch)
'plus a "valid transmission" input due to delay in outputs following buttons
'and a 4-coil unipolar stepper motor
'
'
'personal setting notes:  my own speed constant is 3300 (3388) octal (8=0)
'                         my own backlash constant is 017d or 821 octal
'
'
'The gear ratio (speed) is completely customizable from the telescope
'focus knob gear TO the stepper motor shaft.
'
'Switches are assigned to PIC port A as follows:
'A5: Unused (MCLR')
'A4: limit switch
'A3: keypress D3
'A2: keypress D2
'A1: keypress D1
'A0: keypress D0
'note that the presence of the limit switch IS required for operation, as without
'it, there is no reference for either the outer safety limit or the preset. All
'switch input pins of the PIC have a 10k pulldown resistor. Switches are
'active high.
'
'B0: coil 1
'B1: coil 2
'B2: coil 3
'B3: coil 4
'B4: LED
'B5: servo1 out
'B6: servo2 out / ICSP in
'B7: VT (for wireless remote) / ICSP in
'
'The stepper coils are connected and gearing is oriented so that sequencing
'coil 1-2-3-4 steps the focuser in the OUTWARDS direction.
'
'The wireless hand controller has 8 buttons, multiplexed to a 4-line input
'In addition, all inputs from the hand controller can
'be diode-OR'd with Parallel port inputs from a PC, 
'to use AstroSnap for remote PC and autofocus control respectively.
'the VT input is a "valid transmission" from the remote receiver, and is
'used because of an inherent delay in the outputs turning off after
'the remote pushbutton is released (approx. .6 seconds).
'
'A1=AstroSnap OUT control
'A0=AstroSnap IN control
'
'
'********************************************************************
'*************  WIRELESS Functions are as follows: ******************
'********************************************************************
'single step - one step per press of IN or OUT button after pressing button 4
'fast - move fast toggle on/toggle off with IN/OUT button after pressing button 3
'***NOTE: pause 1 second before moving in/out to allow decoder to reset after
'***the button has been released - otherwise, it may travel too far.

'medium - 1/5 the speed of the above after pressing button 5 (toggle on/off)
'slow - 1/5 the speed of medium after pressing button 7 (toggle on/off)
'
'Store Preset <n> (1-8) - 6xIN (x=1-8)
'press MEM (button 6) followed by 1/2/3/4/5/6/7/8 followed by IN
'LED will light steady. Then press IN again to store or any other button
'to abort storage. LED will flash when stored.
'
'Recall Preset <n> (1-8) - 6xOUT (x=1-8)
'press MEM (button 6) followed by one of the above, followed by OUT
'LED will flash when it has arrived at the preset destination.
'
'Stepper Mode Change - 833x (1-6)
'Press 8,3,3,followed by mode #1,2,3,4,5, or 6
'	Modes are as follows:
'		1:half-step constant-power mode (high resolution)
'		2:single-step constant-power mode (normal resolution)
'		3:dual-coil constant-power mode (normal resolutin, hi-torque)
'		4:half-step power-saver mode (high resolution)
'		5:single-step power-saver mode (normal resolution)
'		6:dual-coil power-saver mode (normal resolution, hi-torque)
'
'Reset - 834
'Press 8,3,4
'
'Reboot - 835
'Press 8,3,5. The LED will light to await the MAXOUT position.
'press OUT to acknowledge and the system will step inward to the limit switch.
'note that presets are retained.
'
'SPEED constant store - 836xxxx (max 7777 (4095) - note that 8=0)
'saves reprogramming to change the stepping speed constant
'the constant is input in the form of a 4-digit octal number.
'This number is stored by the program as the
'pauseus delay (microsecond delay between pulses).
'To input a new delay value, press 8,3,6
'followed by the 4 digit octal sequence.
'(LED will flash to indicate the value has been saved).
'
'SPEED constant recall - 837
'To recall the current timing value (octal), press 8,3,7. It will
'flash on the LED (note: a 0 will be shown as an 8).
'-------------------------------------------------------------------
'******** MOTOR CONTROL SEQUENCES *********
'normally (on power up), the FOCUSER will be enabled.
'
'Servo1 control - 843
'to switch to servo1 in/out control, press 8,4,3. The servo will
'be controlled by the IN/OUT commands until you deactivate it by entering
'another motor control sequence. (LED confirmation of 2 fast flashes)
'
'Servo2 control - 844
'to switch to servo2 in/out control, press 8,4,4. The servo will
'be controlled by the IN/OUT commands until you deactivate it by entering
'another motor control sequence. (LED confirmation of 3 fast flashes)
'
'Back to Focuser control - 845
'to switch back to focuser control, press 8,4,5. (LED confirmation
'of 1 fast flash).
'
'Astrosnap Listen - 857
'press 8,5,7 - spends its time listening for astrosnap
'focus commands on A0,A1. Press PGM again to exit this mode.
'
'RB5 ON - 863
'RB5 OFF - 864
'RB6 ON - 865
'RB6 OFF - 866
'
'Backlash entry - 873
'Enter the backlash constant (0-255, entered in octal - 000-377)
'
'Backlash display - 874
'Display the backlash constant (0-255, entered in octal - 000-377)
'
'Begin Autofocus Routine - 875
'Starts the autofocus routine
'ENSURE you move focus IN beyond its approximate best PRIOR to
'beginning Autofocus.
'Then take a reading with K3CCDtools or some other program that has
'star analysis capability.
'Then at each stop (LED lights up awaiting input), press OUT (better) or IN (worse).
'at the end, the LED will flash several times,
'indicating you're done.
'
'-----------------------------------------------------
'
'Upon first time power-up,
'the PIC Is set For dual coil, power saver mode, AND the LED lights steadily
'to indicate that the user must manually move the focuser to the "maximum out"
'position that they want accessible by the stepper (before the stepper fights
'due to the hard stop and causes you grief). Once the focuser is manually moved to this point,
'press the OUT button. The motor will proceed to step inwards until it reaches
'the ZERO point limit switch. It then memorizes these positions as maxpoints, and
'stores the reference so presets can be stored in EEPROM. Following this (once
'it reaches the zero point), it will await keypad/computer commands.
'
'After the first time powering up, there is no need to move the focuser out to
'the maxpoint, as it is stored in EEPROM. Subsequent power-ups will move the
'focuser in until the ZERO limit switch is activated, then will proceed to
'preset#1 and await further commands. After 3 seconds of no commands, the
'PIC will automatically power-down the stepper coils to preserve power,
'if the pic is in mode 4,5,or 6 (modes 1,2,and 3 are constant power).
'
'
'************************************
'******* BEGIN PICBASIC CODE ********
'************************************
'
'ASSIGN VARIABLES
swstat VAR BYTE 'switch status
oldstat VAR BYTE 'previous switch status for comparison
offset VAR WORD 'stepper motor coil pattern offset for the ZERO position
myst VAR WORD 'stepper coil pattern #
incr VAR BYTE 'increment marker
decr VAR BYTE 'decrement marker
mydelay VAR WORD 'delay between checks of switch status or steps
tconst VAR BIT
servo1 VAR BYTE 'servo1 center
servo2 VAR BYTE 'servo2 center
nadahold VAR WORD 'timer counter for powering down coils if no switches pressed
hold VAR BYTE 'timer counter for seeing how long keys are pressed
coils VAR BYTE(8) 'coil patterns loaded from EEPROM
temp VAR BYTE 'misc variable
temp1 VAR BYTE 'ditto
temp2 VAR WORD '16 bit temp variable
preset VAR WORD 'preset location (stored to/retrieved from EEPROM)
maxout VAR WORD 'maximum out location (stored to/retrieved from EEPROM)
posn VAR WORD 'current position
stpmd VAR BYTE 'mode 0,1,or 2
poff VAR BIT 'preset released flag
pcount VAR BYTE 'tracks # of times PRESET button pressed
ssel VAR BYTE 'temporary variable
sdir VAR BYTE 'servo direct byte
offignore VAR BIT 'constant coilpower mode
mybits VAR BYTE 'user-defined bits status for PB5,6,7
storedelay VAR WORD 'the stored time delay for between pulses
iseekdelay VAR WORD 'the initial seek limit switch delay between pulses
keybuf VAR WORD 'key buffer
bufcount VAR BYTE 'key buffer digit counter
buftarget VAR BYTE 'the number of keys for the current command (target)
aborttime VAR WORD 'countdown before key buffer is cleared (aborted command)
targetdef VAR BYTE (8) 'target digit definitions 1-8
speed VAR BYTE 'current speed selected
astrosnap VAR BIT 'astrosnap mode flag
stayon VAR BIT 'toggle status flag
octalresult VAR WORD
servo1enable VAR BIT
servo2enable VAR BIT
diagbyte VAR BYTE
vt VAR PORTB.7 'valid transmission
backlash VAR BYTE 'backlash constant
afocus VAR BIT 'autofocus mode flag
famt VAR BYTE 'autofocus amount
direct VAR BIT 'direction flag

'DEFINE DATA IN EEPROM
DATA 1,3,2,6,4,12,8,9 'half step sequence (EEPROM 0-7)
DATA 1,1,2,2,4,4,8,8 'single step sequence (EEPROM 8-15)
DATA 3,3,6,6,12,12,9,9 'dual coil sequence (EEPROM 16-23)
DATA 0,0 'maxout (EEPROM 24,25)
DATA 0,0,0,0,0,0,0,0,0,0'preset1,2,3,4,5 (EEPROM 26,27|28,29|30,31|32,33|34,35)
DATA 0,0,0,0,0,0,0,0,0,0'preset 6,7,8,9,10 (EEPROM 36,37|38,39|40,41|42,43|44,45)
DATA 5 'mode (EEPROM 46) startup in dualcoil power saver mode
DATA 82,13 'default mydelay value (EEPROM 47, 48)
DATA 128,128 'default midway for servo1, servo2 (EEPROM 49, 50) 
DATA 0 'default value for backlash (EEPROM 51)

'********PROGRAM START*******
powerup: 'powerup sequence
OSCCON=$60 'delete if using 16F84
ANSEL=0 'all inputs assigned as digital
	'first assign port pins for I/O
TRISA=63 'PortA all input - note A5 is input only!
TRISB=128  'PortB all output but PB7 (VT)
PORTB=0
CLEAR

'Power-up LED flash
PORTB.4=1
PAUSE 1000

GOTO newprogram
'
'
'****************************************************************
'****** Stepper coil sequence loading subroutine ****************
'****************************************************************
stpmdload: 'data loading subroutine for stepper coil sequences
WRITE 46, stpmd 'store for future reference
IF stpmd>2 THEN 'power saver mode
'unset the constant power flag and revise the mode
	offignore=0
	stpmd=stpmd-3
ELSE
	offignore=1
ENDIF
FOR temp=0 TO 7
	READ (stpmd*8+temp), coils(temp)
NEXT temp
RETURN
'
'
'****************************************************************
'********** Flash preset subroutine *****************************
'****************************************************************
pflash: 'flash preset number (subroutine)
PAUSE 500
FOR temp=1 TO pcount
	PORTB.4=1:PAUSE 125:PORTB.4=0:PAUSE 125
NEXT temp
RETURN
'
'
'****************************************************************
'*********** Start main program *********************************
'****************************************************************
newprogram:       
CLEAR
targetdef(0)=1 '1=out is a single keypress action
targetdef(1)=1 '2=in is a single keypress action
targetdef(2)=1 '3=set speed high (single keypress)
targetdef(3)=1 '4=single-step is a single keypress action
targetdef(4)=1 '5=medium speed is single keypress action
targetdef(5)=3 '6=memory is a 3-key action (MEM_IN/OUT_#)
targetdef(6)=1 '7=slow speed is a single keypress action
targetdef(7)=3 '8=3 key minimum with modifiers
'first, turn off the LED.
PORTB.4=0
PAUSE 1000
'read the maxout and step mode values
READ 24,maxout.Byte0:READ 25,maxout.Byte1:READ 46,stpmd
GOSUB stpmdload 'load the step array
'next, check to see if maxout is defined
IF maxout=0 THEN 'define the maximum out point
    PORTB.4=1			'turn on LED
userwait1:
	IF (PORTA & 15) <> 1 THEN GOTO userwait1 'and wait until OUT button is pressed
    PORTB.4=0			'turn off the LED and proceed
ENDIF
'now move in at predefined rate until limit switch is pressed
IF stpmd>0 THEN 			'if single step or dual coil, count down by 2's
	decr=2
ELSE
	decr=1
ENDIF
READ 47,storedelay.Byte0:READ 48,storedelay.Byte1
iseekdelay=storedelay*5/4
posn=32000				'arbitrary value to start at

find01:					'beginning of loop
myst=posn.Byte0 & 7 	'neat way to figure out the stepper sequence pointer
PORTB=coils(myst)		'output the coils
PAUSEUS iseekdelay
'look for the limit switch being pressed
IF PORTA.4=1 THEN GOTO found01 'we've found it
posn=posn-decr			'not found yet, decrement position
GOTO find01				'loop around until you get it

found01:					'limit switch activated - now carry on
offset=myst				'coil pattern table offset for 0 position
IF maxout=0 THEN
    maxout=32000-posn
    'store the maxout position
    WRITE 24,maxout.Byte0:WRITE 25,maxout.Byte1
ENDIF
posn=0  'set the position to 0 at the limit switch


'now goto the first preset position
READ 26, preset.Byte0	'get default preset (#1)
READ 27, preset.Byte1
GOSUB myprset
READ 51, backlash

'initialization complete. Now await user input.
'--------------------------------------------------
clrkeys:
bufcount=0:buftarget=0
keybuf=0
'---------------------------------------------------         
keygrab: 'command receive input loop
'vt is valid transmission
'loop until valid transmission
IF PORTA.4=1 THEN		'limit switch activated - define 0 point
	posn=0
	offset=myst
ENDIF

IF astrosnap=1 THEN GOTO astroprocess
IF vt=0 THEN 
    PAUSE 10 'wait 1/100 sec
    aborttime=aborttime+1
    IF aborttime=500 THEN
        'waited too long - abort accumulated buttons
        keybuf=0:buftarget=0:bufcount=0
        aborttime=0
        IF servo1enable=1 THEN GOTO keygrab
        IF servo2enable=1 THEN GOTO keygrab
        IF offignore=0 THEN
            PORTB.0=0:PORTB.1=0:PORTB.2=0:PORTB.3=0 'turn off coils to save power
        ENDIF
    ENDIF
    GOTO keygrab
ENDIF
'valid transmission detected - read the code
PAUSE 10 '1/100 second for output to be decoded and stable
aborttime=0
swstat=PORTA
swstat=swstat & 15
'now wait for the button to be released

waitvt:
IF vt=1 THEN GOTO waitvt 'loop until key released
'button has been released
PAUSE 50 '20ms debounce time for VT

'process the button
keybuf=keybuf*10+swstat
IF bufcount=0 THEN
    buftarget=targetdef(swstat-1)'if it's the first digit, figure how many we'll have
ENDIF
    
bufcount=bufcount+1 'increment the count buffer

IF bufcount=buftarget THEN 
    GOTO interpret 'count achieved - process
ENDIF
'we're still collecting keypresses - accumulate the abort timer
'loop back and check for input again
GOTO keygrab

'----------------------------------------
 astroprocess:
 'astroprocess IN/OUT commands go here
 'process OUT(1), IN(2) and EXITMODE(8)
swstat=PORTA
swstat=swstat & 15
 IF swstat=1 THEN 'OUT command
    incr=1
    mydelay=storedelay/5
    tconst=0
    GOSUB mover1
 ENDIF
 IF swstat=2 THEN 'IN command
    incr=0
    mydelay=storedelay/5
    tconst=0
    GOSUB mover1
 ENDIF
 IF swstat=8 THEN 'quit astrosnap mode
    astrosnap=0
 ENDIF
    
 GOTO keygrab
 '------------------------------------------

'-------------------------------------------
interpret: '(NOT a subroutine)
IF afocus=1 THEN GOTO autofocus
    
    
PORTB.4=1:PAUSE 100:PORTB.4=0:PAUSE 100
'we've hit the buffer target number of keypresses, so process the result.
'
'...........................................
'
IF keybuf=1 THEN 'OUT command *works ok*
    IF servo1enable=1 THEN GOTO servo1moveout
    IF servo2enable=1 THEN GOTO servo2moveout
    'note that astrosnap commands are handled separately!
    'move OUT at current speed
    'if speed is 0 (single step) then step once only.
    IF speed=0 THEN
        IF (direct=1) AND (backlash>0) THEN 'it was moving out, so do the backlash
            mydelay=storedelay:tconst=0
            FOR temp1=1 TO backlash
                incr=1:decr=0
                GOSUB mover1
            NEXT temp1
        ENDIF
        incr=1:decr=0
        mydelay=200:tconst=1
        GOSUB mover1
        GOTO clrkeys
    ENDIF
    'speed is 1, 2, or 3 (1 being slowest)
    'toggle it ON while checking for subsequent keypresses
    incr=1:decr=0
    IF speed=1 THEN mydelay=storedelay*25 'slow
    IF speed=2 THEN mydelay=storedelay*5 'medium
    IF speed=3 THEN mydelay=storedelay 'fast
    tconst=0 'set as pauseus in microseconds
    PAUSE 20 '1/50 second wait for debounce
    
yougoout:
    GOSUB mover1
    incr=1:decr=0
    'continuous move until a key is pressed
    IF vt=0 THEN GOTO yougoout
    PAUSE 600
    GOTO clrkeys

servo1moveout:
    'servo1 control - move servo outwards (servo on PB5)
    PAUSE 1000 'safety waiting period so switch data doesn't cause an abort
servo01:
	PULSOUT PORTB.5, 240 '2.4mS pulse
	FOR temp1=1 TO 5
		PAUSE 3
		swstat=PORTA & 15
		IF swstat>0 THEN
            PAUSE 600
            GOTO clrkeys
        ENDIF
	NEXT temp1
	GOTO servo01

servo2moveout:
    'servo2 control - move servo outwards (servo on PB6)
    PAUSE 1000
servo02:
    PULSOUT PORTB.6, 240 '2.4mS pulse
    FOR temp1=1 TO 5
    PAUSE 3
    swstat=PORTA & 15
    IF swstat>0 THEN GOTO clrkeys
    NEXT temp1
    GOTO servo02   
ENDIF
'
'.............................................
'
IF keybuf=2 THEN 'IN command *works ok*
    IF servo1enable=1 THEN GOTO servo1movein
    IF servo2enable=1 THEN GOTO servo2movein
    'note that astrosnap commands are handled separately!
    'move IN at current speed
    'if speed is 0 (single step) then step once only.
    IF speed=0 THEN
        IF (direct=0) AND (backlash>0) THEN 'previous direction was out, so do backlash first
            mydelay=storedelay:tconst=0
            FOR temp1=1 TO backlash
                incr=0:decr=1
                GOSUB mover1
            NEXT temp1
        ENDIF
        incr=0:decr=1
        mydelay=200:tconst=1
        GOSUB mover1
        GOTO clrkeys
    ENDIF
    'speed is 1, 2, or 3 (1 being slowest)
    incr=0:decr=1
    IF speed=1 THEN mydelay=storedelay*25 'slow
    IF speed=2 THEN mydelay=storedelay*5 'medium
    IF speed=3 THEN mydelay=storedelay 'fast
    tconst=0 'set as pauseus, not pause in ms
    PAUSE 20 '1/50 second for vt to debounce
    
yougoin:
    GOSUB mover1
    'continuous move until a key is pressed
    incr=0:decr=1
    IF vt=0 THEN GOTO yougoin
    PAUSE 600
    GOTO clrkeys

servo1movein:
    'servo1 control - move servo outwards (servo on PB5)
    PAUSE 1000 'safety waiting period so data doesn't cause an abort
servo01in:
	PULSOUT PORTB.5, 60 '.6mS pulse
	FOR temp1=1 TO 5
		PAUSE 3
		swstat=PORTA & 15
		IF swstat>0 THEN GOTO clrkeys
	NEXT temp1
	GOTO servo01in

servo2movein:
    'servo2 control - move servo outwards (servo on PB6)
    PAUSE 1000
servo02in:
    PULSOUT PORTB.6, 60 '.6mS pulse
    FOR temp1=1 TO 5
    PAUSE 3
    swstat=PORTA & 15
    IF swstat>0 THEN GOTO clrkeys
    NEXT temp1
    GOTO servo02in   

ENDIF
'
'...............................................
'
IF keybuf=3 THEN '*works ok
    speed=3 'fast
    GOTO clrkeys
ENDIF
'
'...............................................
'
IF keybuf=4 THEN '*works ok
    speed=0 'single step
    GOTO clrkeys
ENDIF
'
'...............................................
'
IF keybuf=5 THEN '*works ok*
    speed=2 'mid speed
    GOTO clrkeys
ENDIF
'
'...............................................
'
IF keybuf=7 THEN '*works ok*
    speed=1 'slow
    GOTO clrkeys
ENDIF
'
'................................................
'
IF (keybuf/100=6) AND (keybuf//10=1) THEN 'preset recall(memory OUT) *works ok*
    GOSUB myprset
    GOTO clrkeys
ENDIF

GOTO afterpreset 'jump around these subroutines

myprset: 'this is a subroutine so it's accessed originally

pcount=1 'default value if none other match
IF keybuf=611 THEN pcount=1
IF keybuf=621 THEN pcount=2
IF keybuf=631 THEN pcount=3
IF keybuf=641 THEN pcount=4
IF keybuf=651 THEN pcount=5
IF keybuf=661 THEN pcount=6
IF keybuf=671 THEN pcount=7
IF keybuf=681 THEN pcount=8
    
GOSUB pflash 'flash the LED to say you're on your way
READ (24+2*pcount), preset.Byte0
READ (25+2*pcount), preset.Byte1

recheck1:
IF preset=posn+1 THEN GOTO mchek1 'check to see if it's halfstep or not
IF preset=posn-1 THEN GOTO mchek1 'ditto

chkeq1:
IF preset=posn THEN 'we've arrived - throw a party and invite the neighbors
    GOSUB pflash 'or maybe just flash the preset #
    pcount=0
    RETURN
ENDIF

incr=1:decr=0
IF preset<posn THEN
    incr=0:decr=1
ENDIF
mydelay=storedelay:tconst=0 'sets as pauseus 
GOSUB mover1 'act on the step direction setting, then come back here
GOTO recheck1 'are we there yet, are we there yet, are we there yet?
'
mchek1: 'checks the mode to see if it's double-stepping in or out
IF stpmd>0 THEN 'we've arrived so do the flashy thing and exit
    GOSUB pflash
    pcount=0
    RETURN
ELSE
    GOTO chkeq1 'go finish checking and comparing and moving
ENDIF
'
'.......................................
'
afterpreset:
'
'........................................
IF (keybuf/100=6) AND (keybuf//10=2) THEN 'memory STORE command *works ok*
    pcount=1 'default value in case none others match
    IF keybuf=612 THEN pcount=1
    IF keybuf=622 THEN pcount=2
    IF keybuf=632 THEN pcount=3
    IF keybuf=642 THEN pcount=4
    IF keybuf=652 THEN pcount=5
    IF keybuf=662 THEN pcount=6
    IF keybuf=672 THEN pcount=7
    IF keybuf=682 THEN pcount=8
    
    'light the LED until a key is pressed
    PORTB.4=1
    PAUSE 1000 'wait 1 second to release keys
    keyloop:
    'the IN key will store it, any other key will abort storing
    swstat=PORTA & 15
    IF swstat=0 THEN keyloop
    IF swstat=2 THEN 'store it
        WRITE (24+2*pcount), posn.Byte0
        WRITE (25+2*pcount), posn.Byte1
    ENDIF
    'skip out now
    PORTB.4=0 'turn off prompt LED
    pcount=0
    PAUSE 1000 'allow user to remove finger without re-init the button
    GOTO clrkeys
ENDIF
'....................
'Set Step Mode X - key sequence 8331/8332/8333/8334/8335/8336 *works ok*
IF keybuf=833 THEN
finishstep:
    IF vt=0 THEN GOTO finishstep
    PAUSE 50 '20ms for the code to stabilize
    swstat=PORTA & 15
    IF swstat>6 THEN GOTO clrkeys
    FOR diagbyte=1 TO swstat
        PORTB.4=1:PAUSE 200:PORTB.4=0:PAUSE 200
    NEXT diagbyte
    stpmd=swstat-1
    GOSUB stpmdload
    GOTO clrkeys
ENDIF       
'.....................
'Reset - key sequence 834 *works ok*
IF keybuf=834 THEN
    GOTO newprogram
ENDIF
'......................
'REBOOT - key sequence 835 *works ok*
IF keybuf=835 THEN
    WRITE 24,0:WRITE 25,0
    GOTO newprogram
ENDIF
'......................
'Store timing constant - key sequence 836XXXX (octal) max is 7777octal (=4095dec)
IF keybuf=836 THEN
    keybuf=0:bufcount=0
    oldstat=0
    PAUSE 600 'wait for decoder to release last digit
anotherdigit:
    swstat=PORTA & 15
    IF swstat=0 THEN
        aborttime=aborttime+1
        PAUSE 10 'wait 1/100 second
        IF aborttime=500 THEN '5 second timer expired - abort
            aborttime=0
            GOTO clrkeys
        ENDIF
    ENDIF
    IF swstat=oldstat THEN GOTO anotherdigit
    oldstat=swstat
    IF swstat<>0 THEN
        aborttime=0
        IF swstat=8 THEN
            keybuf=keybuf*8
        ELSE
            keybuf=keybuf*8+swstat
        ENDIF
        bufcount=bufcount+1
        IF bufcount=4 THEN GOTO thetimenow
    ENDIF
    GOTO anotherdigit
thetimenow:
    storedelay=keybuf
    'store the new timing thing
    WRITE 47,storedelay.Byte0:WRITE 48,storedelay.Byte1
    PORTB.4=1:PAUSE 100:PORTB.4=0:PAUSE 500
    GOTO clrkeys    
ENDIF
'......................
'Display timing constant - key sequence 837 - *works ok*
IF keybuf=837 THEN
    octalresult=storedelay
    'pflash for pcount times (pflash is a gosub)
    pcount=octalresult/512
    octalresult=octalresult-pcount*512
    IF pcount=0 THEN pcount=8
    GOSUB pflash
    pcount=octalresult/64
    octalresult=octalresult-pcount*64
    IF pcount=0 THEN pcount=8
    PAUSE 1000
    GOSUB pflash
    pcount=octalresult/8
    octalresult=octalresult-pcount*8
    IF pcount=0 THEN pcount=8
    PAUSE 1000
    GOSUB pflash
    pcount=octalresult
    IF pcount=0 THEN pcount=8
    PAUSE 1000
    GOSUB pflash
    pcount=1
    GOTO clrkeys
ENDIF

'..................
'switch to servo1 control mode - key sequence 843
'this changes the interpretation of the IN/OUT keys
IF keybuf=843 THEN
    servo1enable=1
    servo2enable=0
    GOTO clrkeys
ENDIF

'..................
'switch to servo2 control mode - key sequence 844
'this changes the interpretation of the IN/OUT keys
IF keybuf=844 THEN
    servo1enable=0
    servo2enable=1
    GOTO clrkeys
ENDIF

'..................
'switch back to focuser control - key sequence 845
'this changes back to focuser control from servo control
IF keybuf=845 THEN
    servo1enable=0
    servo2enable=0
    GOTO clrkeys
ENDIF

'.................
'switch to Astrosnap mode (non-toggle, no delay) -key sequence 857
'stays in this mode until switched out with 8 key
IF keybuf=857 THEN
    astrosnap=1
    GOTO clrkeys
ENDIF

'.................
'turn RB5 output on (high) - key sequence 863
IF keybuf=863 THEN
    PORTB.5=1
    GOTO clrkeys
ENDIF

'.................
'turn RB5 output off (low) - key sequence 864
IF keybuf=864 THEN
    PORTB.5=0
    GOTO clrkeys
ENDIF

'..................
'turn RB6 output on (high) - key sequence 865
IF keybuf=865 THEN
    PORTB.6=1
    GOTO clrkeys
ENDIF

'..................
'turn RB6 output off (low) - key sequence 866
IF keybuf=866 THEN
    PORTB.6=0
    GOTO clrkeys
ENDIF

'..................
'Store baclkash constant - key sequence 873-XXX (octal) max is 377octal (=255dec)
IF keybuf=873 THEN
    keybuf=0:bufcount=0
    oldstat=0
    PAUSE 600 'wait for decoder to release last digit
anotherdigit1:
    swstat=PORTA & 15
    IF swstat=0 THEN
        aborttime=aborttime+1
        PAUSE 10 'wait 1/100 second
        IF aborttime=500 THEN '5 second timer expired - abort
            aborttime=0
            GOTO clrkeys
        ENDIF
    ENDIF
    IF swstat=oldstat THEN GOTO anotherdigit1
    oldstat=swstat
    IF swstat<>0 THEN
        aborttime=0
        IF swstat=8 THEN
            keybuf=keybuf*8
        ELSE
            keybuf=keybuf*8+swstat
        ENDIF
        bufcount=bufcount+1
        IF bufcount=3 THEN GOTO backlashconst
    ENDIF
    GOTO anotherdigit1
backlashconst:
    backlash=keybuf
    'store the new backlash constant
    WRITE 51,backlash
    PORTB.4=1:PAUSE 100:PORTB.4=0:PAUSE 500
    GOTO clrkeys    
ENDIF
'......................
'
'Display backlash constant - key sequence 874 - *works ok*
IF keybuf=874 THEN
    octalresult=backlash
    'pflash for pcount times (pflash is a gosub)
    pcount=octalresult/64
    octalresult=octalresult-pcount*64
    IF pcount=0 THEN pcount=8
    PAUSE 1000
    GOSUB pflash
    pcount=octalresult/8
    octalresult=octalresult-pcount*8
    IF pcount=0 THEN pcount=8
    PAUSE 1000
    GOSUB pflash
    pcount=octalresult
    IF pcount=0 THEN pcount=8
    PAUSE 1000
    GOSUB pflash
    pcount=1
    GOTO clrkeys
ENDIF

'..................
autofocus:
'Begin autofocus routine - key sequence 875
'it is IMPERATIVE that you move focus IN beyond its approximate best
'PRIOR to starting this routine.
IF keybuf=875 THEN
    portb.4=1:PAUSE 250:portb.4=0:PAUSE 250:portb.4=1:PAUSE 250:portb.4=0
    afocus=1:famt=128 'set autofocus count
    speed=3:mydelay=storedelay 'fast
    tconst=0 'set as pauseus in microseconds
    direct=0 'set original direction to outwards
    'first move OUT 128 steps
    FOR temp1=1 TO famt
        incr=1:decr=0
        GOSUB mover1
    NEXT temp1
    portb.4=1
    'wait for input - is focus better or worse?
    '1(out) means better, 2(in) means worse.
    GOTO clrkeys
ENDIF
IF keybuf=1 THEN 'OUT button is pressed, focus is better
    portb.4=0 'turn off the LED
    'move for another round
    FOR temp1=1 TO famt
        incr=direct ^ 1 :decr=direct
        GOSUB mover1
    NEXT temp1
    portb.4=1 'turn on the LED to prompt
    'wait for input - is focus better or worse?
    GOTO clrkeys
ENDIF
IF keybuf=2 THEN 'IN button is pressed, focus is worse
    portb.4=0 'turn off the LED
    direct=direct ^ 1 'change direction
    
    IF famt=1 THEN 'already at smallest increment, you're finished - back it up one step
        FOR temp1=1 TO famt+backlash
            incr=direct ^ 1:decr=direct
            GOSUB mover1
        NEXT temp1
        afocus=0
        FOR temp=1 TO 10 'flash 10 times to say you're done
            portb.4=1:PAUSE 100:portb.4=0:PAUSE 100
        NEXT temp
        GOTO clrkeys
    ENDIF

    'otherwise, kick the resolution down a notch and continue
    IF famt=2 THEN famt=1
    IF famt=4 THEN famt=2
    IF famt=8 THEN famt=4
    IF famt=16 THEN famt=8
    IF famt=32 THEN famt=16
    IF famt=64 THEN famt=32
    IF famt=128 THEN famt=64
    'move back half the last amount plus backlash
    FOR temp1=1 TO (famt+backlash)
        incr=direct ^ 1 :decr=direct
        GOSUB mover1
    NEXT temp1
    portb.4=1 'turn on the LED to prompt
    'wait for input - is focus better or worse?
    GOTO clrkeys
ENDIF
'******************************************


GOTO clrkeys

mover1: 'sends the update out depending on incr, decr, and mydelay
IF stpmd>0 THEN 'double-step through the table
	incr=incr*2
	decr=decr*2
ENDIF
IF decr>posn THEN
    FOR diagbyte=1 TO 10
        PORTB.4=1:PAUSE 100:PORTB.4=0:PAUSE 100
    NEXT diagbyte
    GOTO clrkeys 'will pass the zero point so don't move
ENDIF

IF posn+incr > maxout THEN
    FOR diagbyte=1 TO 5
        PORTB.4=1:PAUSE 100:PORTB.4=0:PAUSE 100
    NEXT diagbyte
    GOTO clrkeys
ENDIF

'it's a valid move; proceed with moving the stepper
IF incr>0 THEN 'update the direction flag
    direct=0 'outwards
ELSE
    direct=1 'inwards
ENDIF
posn=posn+incr-decr		'new position
myst=(posn+offset) & 7	'stepper table position - isolate lowest 3 bits
mybits=PORTB & 224 'isolate upper 3 bits to retain them
PORTB=coils(myst.Byte0) | mybits  'OR function

IF tconst=1 THEN
    PAUSE mydelay
ELSE
    PAUSEUS mydelay
ENDIF
RETURN

END

'Version History
'5.03   -deleted OLD (hardwired) program, now all is remote control
'       -added backlash determination and compensation
'5.04   -revised ASSISTfocus portion to function properly
'5.05   -added global backlash correction for single step mode