' DUAL-AXIS SOLAR TRACKING ' ' Version 6/08/2011 ' ' Current version http://www.harbornet.com/sunflower/pvdish.html ' Serial port with notebook computer. '**************************************************************** '* Name : sunrun.pbp * '* Author : Sunflower * '* Notice : Copyright (c) 2011 Sunflower * '* : All Rights Reserved * '* Date : 6/08/2011 * '* Version : 1.0 * '* Notes : 18F4620 PICmicro * '* : * '**************************************************************** ' Objective: To point solar dish approximately at sun for safety when sun is not visible, and exactly at sun when sun is visible. ' Compiles with PIC BASIC PRO sold by melabs.com ' melabs.com Programmer (meProg) Options/More Options/Low Voltage Erase + Options/More Options/Program not Data ' (preserves old dish-data during reprogramming) ' Changes for internal oscillator, if desired. 'INCLUDE "P18F4620.INC" ; MPASM Header ' __CONFIG _CONFIG1H, _OSC_INTIO67_1H & _FCMEN_OFF_1H & _IESO_OFF_1H ' Tiny PIC Bootloader = http://www.etc.ugal.ro/cchiculita/software/picbootloader.htm ' Tiny modifications tinybld18F4620_zigbee.asm for bootloading with internal oscillator ' ' xtal EQU 4000000 ; set for 4 mhz you may want to change: _XT_OSC_1H _HS_OSC_1H _HSPLL_OSC_1H ' baud EQU 9600 ; the desired baud rate ' ' __CONFIG _CONFIG1H, _OSC_INTIO67_1H & _FCMEN_OFF_1H & _IESO_OFF_1H ' __CONFIG _CONFIG2H, _WDT_OFF_2H & _WDTPS_512_2H ' __CONFIG _CONFIG3H, _MCLRE_ON_3H & _PBADEN_OFF_3H ; & _CCP2MX_PORTBE_3H ; NOTE CCP2MX_PORTBE_3H COMMENT OUT ' movwf RCSTA ' (added after movwf RCSTA) ' movlw b'01100000' ;ADDED to set osccon to 4mhz internal ' movwf OSCCON ' ' http://www.picbasic.co.uk/forum/showthread.php?t=14434&s=de0290749fc5ff853a59643b070c910c ' ' ' Default Re: Port Variables act like Constants ' ' ' *** MUST DO THIS *** ' ' The PORTL and PORTH aliases are used for the PIN numbers (0-15) ' PORTL is 0-7 and PORTH is 8-15. ' Look in the .bas file for the chip you are using. ' If it's a 18F4620 then open c:\pbp\18F4620.bas ' PORTL VAR PORTB ' PORTH VAR PORTD ' TRISL VAR TRISB ' TRISH VAR TRISD ' ' PORTB AND PORTD can now be referenced by variables. ' http://www.picbasic.co.uk/forum/showthread.php?t=14493&s=de0290749fc5ff853a59643b070c910c ' ' ' Olimex PIC-P40 carrier board http://www.olimex.com/dev/pic-p40.html ' Connect CTS with RTS on MX232. ' ' Dual MC33926 Motor Driver HBridge http://www.pololu.com/catalog/product/1213 ' Ground M1 PWM/D1 and ground M2 PWM/D1 ' ' Physical pin connections: (all motor connections via HBridge) ' ' 1 - Push button ground to reset ' 2 - LED on carrier board ' 3 - 230 ohm resistor ~ Wind sensor + 0.1mf capacitor ' 10 - Olimex push button on board pulled high 10k ' 15 - Enable motors (EN) ' 16 - Altitude pwm (M2 PWM/D2) ' 17 - Azimuth pwm (M1 PWM/D2) ' 18 - DS1307 clock (I2C SCL pin), pull up 4.7k ' 19 - Azimuth motor (M1 IN1) ' 20 - Azimuth motor (M1 IN2) ' 21 - Altitude motor (M2 IN1) ' 22 - Altitude motor (M2 IN2) ' 23 - DS1307 clock (I2C SDA pin), pull up 4.7k ' 24 - Extra I/O pin ' 25 - Connect to MX232 RX ' 26 - Connect to MX232 TX ' 27 - 230 ohm resistor ~ then photoresistor parallel with 1mf capacitor to ground (azimuth) ' 28 - 230 ohm resistor ~ then photoresistor parallel with 1mf capacitor to ground (azimuth) ' 29 - 230 ohm resistor ~ then photoresistor parallel with 1mf capacitor to ground (altitude) ' 30 - 230 ohm resistor ~ then photoresistor parallel with 1mf capacitor to ground (altitude) ' 33 - Memsic 2125 accelerometer (azimuth) ' 34 - Memsic 2125 accelerometer (azimuth) ' 35 - Switch 2, pull up 10,000k ' 36 - Memsic 2125 accelerometer (altitude) ' 37 - Memsic 2125 accelerometer (altitude) ' 38 - Switch 1, pull up 10,000k ' ' Memsic 2125 accelerometers are installed on edge, like a rolling coin. The spin around the vertical post is translated to a ' horizontal axis containing the azimuth accelerometer (a cable wrapped around the post and around the horizontal drum). ' ' The 4 photoresistors should be +/- 20% of averaged value. The eye is constructed with a glass ball with the focus over a razor-like divider ' separating two sensors for each eye. One eye is for azimuth and the other eye is for altitude. Tracking sensitivity is < +/-0.004 degrees. DEFINE LOADER_USED 1 ' Bootloader space DEFINE OSC 4 ' Tell program 4 MHz timing DEFINE PULSIN_MAX 1000 ' TIMEOUT LIMIT FOR RCTIME and PULSIN COUNT 10us units @ 4MHz 50ms timeout define HSER_CLROERR ' Automatic clear communications error ADCON1 = %00001111 ' Make AN0-AN12 digital OSCCON = %01100000 ' Internal oscillator speed 4 MHz WDTCON = 1 ' Enable watchdog LED VAR PORTA.0 Wind_pin var PORTA.1 ' Wind sensor grounds charged capacitor at 25 mph. ' A/B pins reversible -- ' PORTB.0 Azimuth Sensor A/B = 0 ' Rotate X1 Accelerometer MEMSIC 2125 ' PORTB.1 Azimuth Sensor A/B = 1 ' Rotate Y1 Accelerometer MEMSIC 2125 sw1 var PORTB.2 'Switch1 ' PORTB.3 Altitude Sensor A/B = 3 ' Rotate Y2 Accelerometer MEMSIC 2125 ' PORTB.4 Altitude Sensor A/B = 4 ' Rotate X2 Accelerometer MEMSIC 2125 sw2 var PORTB.5 'Switch2 (Also programming pin LVP, disabled in configuration fuses) ' PORTB.6 Programming Pin ' PORTB.7 Programming Pin ' Warm var PORTC.0 ' Motors HBridge enable/sleep, one enable for both motors. ' PORTC.1 HPWM channel 2 altitude motor speed ' PORTC.2 HPWM channel 1 azimuth motor speed ClockPin var PORTC.3 ' I2C SCL ' I2C DataPin var PORTC.4 ' I2C SDA ' I2C - DS1307 clock ' PORTC.5 Extra wire or second motor enable ' PORTC.6 TX HSEROUT RS232 ' PORTC.7 RX HSERIN RS232 ' PORTD.0 Azimuth Motor A/B = 8 ' PORTD.1 Azimuth Motor A/B = 9 ' PORTD.2 Altitude Motor A/B = 10 ' PORTD.3 Altitude Motor A/B = 11 ' PORTD.4 Azimuth Eye A/B = 12 ' RC Time photoresister parallel with 1 mf capacitor. ' PORTD.5 Azimuth Eye A/B = 13 ' RC Time photoresister parallel with 1 mf capacitor. ' PORTD.6 Altitude Eye A/B = 14 ' RC Time photoresister parallel with 1 mf capacitor. ' PORTD.7 Altitude Eye A/B = 15 ' RC Time photoresister parallel with 1 mf capacitor. time VAR Word ' 1440 minutes per day old_time var word ' Previous minute days var word ' Number of days declination VAR Byte ' +/- 17 Brads (+/- 24 degrees) old_declination VAR BYTE ' Declination used as time stamp of old memory. azMotor var byte ' For controlling forward/reverse pin assignments after istallation. altMotor var byte ' Software rewires motors and sensors by optionally swapping A with B. azSensor var byte altSensor var byte azEye var byte altEye var byte azMotor_A var byte azMotor_B var byte altMotor_A var byte altMotor_B var byte azSensor_A var byte azSensor_B var byte altSensor_A var byte altSensor_B var byte azEye_A var byte azEye_B var byte altEye_A var byte altEye_B var byte flag VAR word SIGNa VAR FLAG.0 SIGNb VAR FLAG.1 solar_noon var flag.2 Azimuth_Stow var flag.3 Altitude_Stow var flag.4 flag_north var flag.5 display var flag.6 reboot var flag.7 azimuth_command VAR FLAG.8 altitude_command VAR FLAG.9 azimuth_go VAR FLAG.10 altitude_go VAR FLAG.11 azimuth_spin VAR FLAG.12 altitude_spin VAR FLAG.13 azimuth_on VAR FLAG.14 altitude_on VAR FLAG.15 flag2 var word sun_overhead VAR flag2.0 cloudy VAR flag2.1 Focus VAR flag2.2 read_flag VAR flag2.3 write_flag VAR flag2.4 tropic_summer VAR flag2.5 dark VAR flag2.6 stow_write VAR flag2.7 MoreBits var byte Fix var MoreBits.0 No_Stow var morebits.1 test var byte ' Menu character click var word ' Test clock pointer VAR word second var byte previous_second var byte smooth var byte k var byte ' Scale constant derived from sensor installation z var word ' Scale constant derived from sensor installation f var word ' Scale constant derived from sensor installation Azimuth_Memory VAR BYTE Altitude_Memory VAR BYTE Azimuth_sensor VAR Byte Altitude_sensor VAR Byte Azimuth_Path VAR BYTE ' Brads from solar position algorithm Altitude_Path VAR BYTE ' Brads from solar position algorithm Azimuth_Path_sense VAR BYTE Altitude_Path_sense VAR BYTE Azimuth_Destination VAR BYTE Altitude_Destination VAR BYTE dawn var byte ' Azimuth sensor location of calculated sunrise AM VAR word ' Time that sun crosses from north to south PM VAR word ' Time that sun crosses from south to north Latitude var byte ' 18.5 degrees Pune = 13 brads '47 degrees Tacoma = 33 brads (binary radians 0-255 in 256 circle) due_north var byte ' Azimuth sensor value of Polaris star. due_south var byte ' Azimuth sensor value of due south due_up var byte ' Altitude sensor value straight up minXY var byte ' Before due north in the west if in tropics. maxXY var byte ' After due south in the northwest minZ var byte ' Limit switch down maxZ var byte ' Limit switch up stow var byte ' Reason(s) for stow Dwell VAR byte ' Wait after eye tracking before memory tracking Azimuth_Motor VAR byte ' Number of minutes motor is on without detectable movement. Altitude_Motor VAR byte ' Number of minutes motor is on without detectable movement. Azimuth_Move VAR byte ' Last location of moving motor Altitude_Move VAR byte ' Last location of moving motor SpeedAz var byte ' Motor speed ~ duty cycle of PWM SpeedAlt var byte ' Motor speed ~ duty cycle of PWM work VAR Word work1 VAR Word work2 VAR Word work3 VAR Word work4 VAR Word work5 VAR Word work6 VAR WORD work7 VAR WORD work8 var word WorkByte var byte WB VAR BYTE WB1 VAR BYTE WB2 VAR BYTE dummy var byte lat var word ' Work variables for solar path algorithm alt VAR word az VAR word h VAR BYTE prior VAR BYTE AMx VAR BYTE PMx VAR BYTE sec VAR BYTE ' Time variables minute VAR BYTE hour VAR BYTE day VAR BYTE date VAR BYTE month VAR BYTE year VAR BYTE north VAR WORD 'Up Eye variables south VAR WORD 'Down east VAR WORD 'Left west VAR WORD 'Right xRaw VAR word ' Accelerometer data yRaw VAR word ' Accelerometer data dat2 VAR BYTE ' Memory dead reckoning variables. dat1 VAR BYTE azPast VAR BYTE altPast VAR BYTE azFuture VAR BYTE altFuture VAR BYTE yes con 1 ' English no con 0 up CON 1 down CON 0 right CON 1 left CON 0 HiPulse CON 1 LoPulse CON 0 sun CON 75 ' Sunny if one eye sees sun. sun2 CON 85 ' Sunny if both eyes see bright. sun_Gap CON 2 ' If eye difference beyond gap then go Azimuth_Gap CON 2 ' Accelerometer +/- error. Altitude_Gap CON 2 ' Accelerometer +/- error. ON_SUN CON 50 ' If both eyes < ON SUN then focused in clear sun, record position to memory Too_Bright CON 16 ' Eye failure. danger CON 16 ' Brads off calculated solar path and tracking the wrong way. drift CON 2 ' Value difference of old declination indicating memory too old to use Search CON 8 ' Number of 4 minute units to search forward and backwards for memory dead reckoning memory gaps. orientate CON 64 ' Brads of azimuth for night park. safe CON 20 ' Brads of altitude for night park. rampAz CON 8 ' Unit of azimuth motor speed increase per second rampAlt CON 16 ' Unit of altitude motor speed increase per second startAz CON 96 ' Minimum azimuth motor start speed startAlt CON 112 ' Minimum altitude motor start speed maxAz CON 208 ' Maximum azimuth motor speed, out of 255 units maxAlt CON 255 ' Maximum altitude motor speed, out of 255 units freq con 20000 ' PWM frequency, make high to avoid ultrasonic whine ' Top of the program PAUSE 10 CLEAR ' Must clear all varibles on startup PAUSE 1 HIGH LED ' LED PAUSE 1 TRISD = %11110000 ' Motor HBridge inputs made low from outputs PortD.0 to PortD.3 TRISC = %11111000 ' Motor PWM and enable made low from outputs PortC.0 to PortC.2 IF sw1 = 0 THEN No_Stow = yes ' Dish will not stow for error or wind. IF SW2 = 0 THEN display = yes ' If switch flipped before power up then serial display enabled. ' Hardware backdoor diagnostics. Enables communication. ' sw1 = 1 Switches pulled up default at power start-up (flipped away from wires). Flipped towards wires enables safety stow ' sw2 = 1 If not default switch setting at power up then serial display port RS232 enabled. ' *** Switch operation *** ' ' Power switch on flip towards wires. ' Use power switch for on/off before disconnecting / connecting power to prevent corruption of time clock data. ' Press push button to reset power. Also press within one second when flash bootloading. ' ' If both direction switches in default position (away from wires) before power on then default solar tracking running. ' If one switch flipped towards wires before power on then serial port enabled for communications. Program loop runs 2 seconds slower. ' If other switch flipped towards wires before power on then the dish operates without error and wind stow functions. ' If both switches are flipped to default position after power on then both motors running on automatic. ' If one or the other switch flipped after power on then altitude motor goes up or altitude motor goes down and azimuth motor off. ' If both switches are flipped off default position after power on then both motors off. ' ' After power on or after reset button then configuration switches control motors ' so after display communications and/or no-stow functions have been configured on power up then ' flip configuration switches to default position (away from wires) to enable motors for automatic tracking. ' ' Must reset power to clear stow flags or to enable / disable serial port display and to enable / disable no-stow functions. Top: read 1000, WORD lat, due_north, due_south, due_up, minxy, maxxy, minz, maxz, dummy, azmotor, altmotor, azsensor, altsensor, azeye, alteye ' Azimuth sensor starts >0 due north (>0 due west in equatorial latitudes) then increases clockwise and ends <255 north by nortwest. ' Altitude sensor starts at >0 horizon (should be something like 36) and ends <255 vertical (something like 100). ' Binary radians 256 Brads = 360 Degrees ' READ dish specific values entered during installation using the command Install Limits. ' Use Polaris or Google maps to determine true north and south. ' lat is site latitude in degrees times 10. ' due_north, due_south, due_up, calibrates dish position sensors. ' minxy, maxxy, minz, maxz, are travel limit switches. ' azMotor, altMotor, azSensor, altSensor, azEye, altEye, are stored flags that reverse wires on pins as needed. ' ' Solar algorithm output -- ' Horizontal is 0 brads. ' Vertical is 64 brads. ' Due north is 0 brads or Due north is 128 brads for negative latitudes. ' Due east is 64 brads. ' Due south is 128 brads or Due south is 0 brads for negative latitudes. ' Due west is 192 brads. azMotor_A = 8 ' Software will reverse wires if motor or sensor installed backwards. 0 to 7 = PORTB.0 to PORTB.7 azMotor_B = 9 ' Configured in Install subroutine. Used in Set_Motors HBridge subroutine. 8 to 15 = PORTD.0 to PORTD.7 if azMotor then azMotor_A = 9 azMotor_B = 8 endif altMotor_A = 10 altMotor_B = 11 if altMotor then altMotor_A = 11 altMotor_B = 10 endif azSensor_a = 0 azSensor_B = 1 if azSensor then azSensor_a = 1 azSensor_B = 0 endif altsensor_a = 4 altsensor_b = 3 if altsensor then altsensor_a = 3 altsensor_b = 4 endif azeye_a = 12 azeye_b = 13 if azeye then azeye_a = 13 azeye_b = 12 endif altEye_a = 14 alteye_b = 15 if alteye then alteye_a = 15 alteye_b = 14 endif SPEEDAZ = 0 ' Stop motors on program startup hpwm 1, SPEEDAZ, freq ' Set PMW to HBridge pause 1 SPEEDALT = 0 hpwm 2,speedalt, freq ' Set PMW to HBridge pause 1 low azmotor_a ' Brake azimuth motor pause 1 low azmotor_b azimuth_on = no pause 1 low altmotor_a ' Brake altitude pause 1 low altmotor_b altitude_on = no pause 1 low warm pause 1 Latitude = (((ABS lat) ** 46602) + 5) / 10 ' 128/180*65535 +5 nearest integer IF LAT.15 THEN Latitude = -Latitude ' If south of the equator. k = due_up - 64 'Brad altitude = Sensor altitude - k, Sensor altitude = Brad altitude + k Due_up should be > = 80 f = (256 * 128) / (due_south - due_north) 'Brad azimuth = (sensor - due_north) */ f z = (256 * (due_south - due_north)) / 128 'sensor azimuth = due_north + (brad azimuth */ z) 'If tropic_summer AND Azimuth_Path > 128 THEN sensor azimuth = due_north - ((256 - brad azimuth) */ z) FIX = 1 reboot = 1 ' Manual drive and power reset flags high wind_pin ' Charge capacitor connected to wind detector. pauseus 100 INPUT wind_pin goto no_delay main: IF azimuth_on = no AND altitude_on = no AND DISPLAY = NO AND sw1 and sw2 THEN IF dark THEN SLEEP 120 IF cloudy THEN SLEEP 10 NAP 7 ' Nap 2 seconds else pause 1000 ENDIF IF DISPLAY THEN PAUSE 2000 no_delay: TOGGLE LED IF wind_pin = 0 then ' If wind detector discharged capacitor then stow stow = $FF altitude_stow = yes endif high wind_pin ' Charge capacitor connected to wind detector. pauseus 100 INPUT wind_pin old_time = time ' Clock ticking Altitude_destination = 0 ' Will be zero when eyes command. Azimuth_destination = 0 ' Will be zero when eyes command. GOSUB Get_Sensors ' Dish position from accelerometers on edge rolling like a coin. GOSUB Get_Time ' Number of minutes since midnight, and number of days since January 1 If old_time > time or reboot = 1 then ' New day routines below, once daily or when reboot. gosub Get_Declination ' 8 bit Brads tropic_summer = 0 if declination.7 = 0 and declination >= Latitude THEN tropic_summer = 1 ' Sun always in north summer tropics. work8 = time gosub Get_flags_north ' Flags times of arctangent crossover from north to south and from south to north time = work8 gosub get_path ' Get current sun position. flag_north = 1 IF time > AM AND time < PM THEN flag_north = 0 ' Sun in the south mid day. if tropic_summer THEN flag_north = 1 ' Sun always in north in the tropic during summer. IF flag_north = 1 THEN Azimuth_Path = PRIOR ' Azimuth_Path = arctangent, prior = the other arctangent ~ the one in the north. wb = Altitude_Path + 1 ' Adding 1 covers sunrise just below the horizon. Below the horizon is 255. if dark and time < 720 and wb < 68 then dark = no ' Sunrise! WB not big, like 255. solar_noon = 1 ' Time adjust to solar noon open for automatic update when sun due south or due north. Clock is solar noon time, not clock time. reboot = 0 ' Reboot done sun_overhead = no if Altitude_Path > 60 and Altitude_Path < 68 then sun_overhead = yes ' Used to disable azimuth tracking error detection during overhead sun. endif 'goto skipps ' This routine between skipps fills tracking memory with test data 'for time = 120 to 1317 ' memory space 2 AM to 10 PM ' gosub get_path ' Solar position calculator ' flag_north = 1 ' IF time > AM AND time < PM THEN flag_north = 0 ' Sun in the south ' if tropic_summer THEN flag_north = 1 ' Sun always in north ' IF flag_north = 1 THEN Azimuth_Path = PRIOR ' North arctangent ' if time < 120 or time > 1317 then skipping ' Memory space 2 AM to 10 PM ' IF Altitude_path > 68 THEN SKIPPING ' No fake tracking data below the horizon ' Azimuth_Path_sense = due_north + (Azimuth_Path */ z) ' Converts calculated Path Brads to specific dish sensor type data. ' if tropic_summer and Azimuth_Path > 127 then Azimuth_Path_sense = due_north - ((256-Azimuth_Path) */ z) ' If tropical sun crosses north zero going west. ' Altitude_Path_sense = Altitude_Path + k ' Converts calculated Path Brads to specific dish sensor type data. ' Pointer = ((Time / 4 )-30)*3 'EEPROM locations 0 to 899 ' WRITE pointer, Azimuth_path_sense, Altitude_path_sense, declination ' ' hserout [7] ' skipping: 'next time 'goto ending ' END 'skipps: if time.0 <> old_time.0 then 'new one minute routines gosub get_path 'load current PATH location flag_north = 1 IF time > AM AND time < PM THEN flag_north = 0 ' Sun in the south if tropic_summer THEN flag_north = 1 ' Sun always in north IF flag_north = 1 THEN Azimuth_Path = PRIOR ' North arctangent if dark and time < 720 and Altitude_Path < 68 then dark = no ' Sunrise! if Altitude_Path > 60 and Altitude_Path < 68 then sun_overhead = yes ' Used to disable azimuth tracking error detection during and after overhead sun. if dwell then dwell = dwell - 1 ' Countdown tranistion from eyes to memory tracking. IF Azimuth_Motor THEN Azimuth_Motor = Azimuth_Motor + 1 ' Count minutes motor running IF Altitude_Motor THEN Altitude_Motor = Altitude_Motor + 1 ' Count minutes motor running IF Azimuth_Motor = 4 THEN ' = 4 could be as little as 2 minutes, start at end of minute 1 and end begining minute 4. wb = Azimuth_sensor - azimuth_move ' Distance moved IF ABS wb <= Azimuth_Gap THEN ' If no movement within sensor error stow.3 = 1 ' Azimuth motor failed to move tag ########### Altitude_Stow = yes ELSE Azimuth_Motor = 1 ' Else movement detected, reset motor counting for continuing motor checking. azimuth_move = Azimuth_sensor ' Reset start location for checking movement distance. ENDIF ENDIF IF Altitude_Motor = 4 THEN ' = 4 could be as little as 2 minutes, start at end of minute 1 and end begining minute 4. wb = Altitude_sensor - altitude_move ' Distance moved IF ABS wb <= Altitude_Gap AND Altitude_sensor < (maxZ - (Altitude_Gap * 2)) AND Altitude_sensor > (minZ + (Altitude_Gap * 2)) THEN 'If no movement within sensor error * 2 stow.4 = 1 ' Altitude motor failed to move tag (check limit switches) ########### Azimuth_Stow = yes ELSE Altitude_Motor = 1 ' Else movement detected, reset motor counting. altitude_move = Altitude_sensor ' Reload start location for checking movement distance. ENDIF ENDIF endif if time.2 <> old_time.2 then ' New four minute routines write_flag = 1 ' Enable memory write read_flag = 0 ' Enable memory read endif Azimuth_Path_sense = due_north + (Azimuth_Path */ z) ' Converts calculated Path Brads to specific dish sensor type data. if tropic_summer and Azimuth_Path > 127 then Azimuth_Path_sense = due_north - ((256-Azimuth_Path) */ z) ' If tropical sun azimuth crosses north 0 to 255 tracking towards west. Altitude_Path_sense = Altitude_Path + k ' Converts calculated Path Brads to specific dish sensor type data. GOSUB Get_Eyes ' Eyes +/- 0.004 degrees. Creates motor commands. Closed loop GOSUB Get_Memory ' Memory of past sun tracking, returns zeros if memory too old. Open loop IF No_Stow = no and Stow THEN Check_Stow IF dark THEN park IF cloudy THEN memory Sunny: ' Eyes command and bypass Helm IF Focus THEN ' Wait after eye tracking before memory tracking. dwell = 8 ' Wait 8 minutes if dish was previously focused. ELSE ' else dwell = 2 ' Wait 2 minutes if previously tracking while sunny. endif GOSUB set_Motors goto run2 ' Print and loop Park: 'Altitude_destination = orientate + k ' orientate brad to sensor data. Fixed Park altitude not on horizon. 'Azimuth_destination = due_north + (safe */ z) ' safe brad to sensor data. Fixed Park azimuth. Altitude_destination = k ' Horizon Azimuth_destination = dawn ' Sunrise GOTO Check_Stow Memory: IF Azimuth_Memory = 0 and altitude_memory = 0 THEN ' If no recent memory then Altitude_destination = Altitude_Path_sense ' tell Helm to use chart from Path Azimuth_destination = Azimuth_Path_sense ' else ' else Altitude_destination = Altitude_memory ' tell Helm to use memory log book Azimuth_destination = Azimuth_memory ENDIF Check_Stow: if No_Stow = Yes then skip_stow IF Altitude_Stow THEN Altitude_destination = due_up - Altitude_Gap ' Point dish up minus sensor error IF Azimuth_Stow = no THEN Azimuth_destination = Azimuth_sensor ' Azimuth_destination = Azimuth_sensor will tell Helm to stop azimuth motor. ENDIF ENDIF IF Azimuth_Stow THEN Azimuth_destination = due_north ' Point dish north IF Altitude_Stow = no THEN Altitude_destination = Altitude_sensor ' Altitude_destination = Altitude_sensor will tell Helm to stop altitude motor. ENDIF ENDIF IF stow AND stow_write = 0 THEN ' Write stow tag only once. Must cycle power to clear stow flags. FOR pointer = 900 TO 992 STEP 4 ' Search for empty location to store stow data READ pointer, wb IF wb = $ff THEN EXIT NEXT pointer wb = time/10 ' Byte size of time/10 WRITE pointer, wb, stow, Azimuth_sensor, Altitude_sensor ' Record time, cause, dish position stow_write = 1 if display then hserout [7] ' Beep computer indicating EEPROM data write ENDIF IF Azimuth_Stow OR Altitude_Stow then Helm Skip_Stow: if dark THEN Helm ' Do not dwell clouds: IF dwell THEN ' If recently tracking sun then dwell before Memory or Path tracking. azimuth_go = no altitude_go = no GOSUB set_motors goto run2 ' Print and loop ENDIF Helm: ' Sets direction and go / no-go for each motor by comparing existing position with desired destination. wb = Azimuth_sensor - Azimuth_destination ' Azimuth... IF ABS wb < Azimuth_Gap AND azimuth_go = no THEN skipper ' If within sensor error and motor is off then leave motor off. IF Azimuth_sensor = Azimuth_destination THEN ' If on target then turn motor off azimuth_go = no GOTO skipper ENDIF azimuth_go = yes ' Else motor on IF Azimuth_sensor < Azimuth_destination THEN azimuth_command = right ' Set direction ELSE azimuth_command = left endif skipper: wb = Altitude_sensor - Altitude_destination ' Altitude... IF ABS wb < Altitude_Gap AND altitude_go = no THEN skippy ' If within sensor error and motor is off then leave motor off. IF Altitude_sensor = Altitude_destination THEN ' If on target then turn motor off altitude_go = no GOTO skippy ENDIF altitude_go = yes ' Else motor on IF Altitude_sensor < Altitude_destination THEN altitude_command = up ' Set direction ELSE altitude_command = down endif skippy: GOSUB Set_Motors goto run2 ' Print and loop. run2: if display then test = 0 ' Holds menu choice. hserout [" Azimuth Altitude Dec",10,13,10] hserout ["Path brads ",dec Azimuth_Path, " ",dec Altitude_Path,10,13,10] hserout ["Path sense ",dec Azimuth_Path_sense, " ",dec Altitude_Path_sense," ",Sdec declination,10,13] hserout ["Memory ",dec Azimuth_Memory, " ",dec Altitude_Memory," ",Sdec old_declination,10,13,10] hserout ["Sensor ",dec Azimuth_sensor, " ",dec Altitude_sensor,10,13] hserout ["Destination ",dec Azimuth_Destination," ",dec Altitude_Destination,10,13," ** "] if cloudy then hserout [" cloudy "] if focus Then hserout [" focus "] if dark then hserout [" dark "] if stow then hserout [" stow ",bin8 stow] if cloudy = no and dark = no then hserout [" sunny "] 'HSEROUT [" **"] hserout [10,13,10," Press (E) for Exit "] ' Test choices. ' decNum = (bcdNum.NIB1 * 10) + bcdNum.NIB0 converts hex byte used for clock display into decimal value. ' bcdNum = (decNum / 10 << 4) + (decNum // 10) converts decimal byte into hex byte suitable for clock display. '*********** only for test without clock connected. ' CLICK = (minute >> 4)*10 + (minute & $F) + 1 ' Convert test clock minute increments to decimal ' IF CLICK = 60 THEN ' CLICK = 0 ' HOUR = ((hour >> 4)*10 + (hour & $F)) ' Convert test clock hour to decimal and increment for 60 minute carryover. ' HOUR = HOUR + 1 ' HOUR = ((HOUR / 10) << 4) + (HOUR // 10) ' Convert test decimal hour back to clock display hex byte. ' ENDIF ' MINUTE = ((CLICK / 10) << 4) + (CLICK // 10) ' Convert test decimal minute back to clock display hex byte. ' click = click + 1 ' Increment test clock minute for next loop. '************** HSERIN 1,timeout,[TEST] ' Hold menu choice -- capital letter or number of displayed choices pause 10 iF TEST = "E" THEN SET_CLOCK Timeout: endif goto main ' Big Loop ending: ' Never happens unless 'ending' is called. end '********************* Get_Sensors: ' Tilt accelerometers on edge like a rolling coin, arctangent of x,y pins returns angle in brad circle 0 to 255. work3 = 0 ' Azimuth sensor work4 = 0 ' Azimuth sensor FOR smooth = 1 TO 32 PULSIN azsensor_a, HiPulse, xRaw work3 = work3 + xRaw PULSIN azsensor_b, HiPulse, yRaw work4 = work4 + yRaw NEXT smooth work3 = work3 / 32 work4 = work4 / 32 IF work3 < 320 OR work3 > 680 OR work4 < 320 OR work4 > 680 THEN STOW.0 = 1 ' Azimuth sensor failure tag ########### Altitude_Stow = yes ENDIF xRaw = work3 MAX 373 MIN 627 xRaw = xRaw - 500 yRaw = work4 MAX 373 MIN 627 yRaw = yRaw - 500 Azimuth_sensor = xRaw ATN yRaw ' Arctangent work3 = 0 ' Altitude sensor work4 = 0 ' Altitude sensor FOR smooth = 1 TO 32 PULSIN altsensor_a, HiPulse, xRaw work3 = work3 + xRaw PULSIN altsensor_b, HiPulse, yRaw work4 = work4 + yRaw NEXT smooth work3 = work3 / 32 work4 = work4 / 32 IF work3 < 320 OR work3 > 680 OR work4 < 320 OR work4 > 680 THEN STOW.1 = 1 ' Altitude sensor failure tag ########### Azimuth_Stow = yes ENDIF xRaw = work3 MAX 373 MIN 627 xRaw = xRaw - 500 yRaw = work4 MAX 373 MIN 627 yRaw = yRaw - 500 Altitude_sensor = xRaw ATN yRaw ' Arctangent return '********************* Get_Time: I2Cread datapin, clockpin, $d1, 0, [sec,minute,hour,day,date,month,year] if display then hserout [12] ' Clear screen if display then hserout [hex2 month," / ",hex2 date," / 20",hex2 year," ",hex hour,":",hex2 minute,":",hex2 sec] ' Hex decimal numbers for clock display, must be converted to decimal to be useful. second = (sec >> 4)*10 + (sec & $F) ' second = (sec.HIGHNIB*10+sec.LOWNIB) time = ((hour >> 4)*10 + (hour & $F))*60 + (minute >> 4)*10 + (minute & $F) ' Number of minutes since midnight. days = (((month >> 4)*10 + (month & $F) - 1) * 305/10 + (date >> 4)*10 + (date & $F)) min 365 ' Number of days since January 1 if display then hserout [" TIME = ",dec time," Thermax",13,10,10] pause 10 endif If time = old_time and second = previous_second then stow.2 = 1 ' Time failure tag ########### Azimuth_Stow = no Altitude_Stow = yes ENDIF previous_second = second ' For checking clock ticking return '********************* Get_Declination: work = 284 + days ' D=23.45 degrees*sin(360 degrees*(284+N)/365)= Declination IF work > 365 THEN work = work - 365 ' Fraction of circle less than 1 work = work ** 45965 ' 256 brads = 360 degrees 256/365 * 65536 = 45965 work = SIN work ' If work.bit15 = 1 then SIN (work) is negative signa = work.BIT15 ' d = ABS work * 16.675 / 127 work = ((ABS work */ 4269)+64) / 127 ' 23.45/360*256=16.675 16.675*256=4269 +64 round up 0.5 IF signa THEN work = 256 - work ' If negative then declination is below equinox 256 (0) declination = work ' Declination byte in brads used in Path routines. return '********************* Get_flags_north: ' Looks at every minute of today's date and calculates arctangent of azimuth. The are two answers so to ' determine which is correct ~ the difference between both is measured minute by minute and the crossover ' is assumed to be near the minimum distance between both answers (occurs East and West). The clock times of these ' twice daily occurances are recorded in variables AM and PM. The sun does not crossover to the north between ' fall equinox and spring equinox for all locations. The sun does not crossover to the south for tropic ' locations when the solar declination is equal or greater than site latitude. ' OSCCON = %01110000 ' Speed up micro to 8 MHz to crunch these numbers. signb = 1 ' Flags sunrise calculation finished. AMX = 127:PMX = 127 ' Variables to hold gap between two arctangent solutions . FOR time = 120 TO 1320 STEP 1 ' For 2:00 AM to 10:00 PM. GOSUB get_PATH ' Solar position algorithm accepts time and declination. Returns two azimuths and one altitude. if signb and altitude_path < 20 then ' Sunrise likely zero but definitely not 255 just below horizon. dawn = due_north + (Azimuth_Path */ z) ' Azimuth location at sunrise. signb = 0 ' Do dawn just once when altitude path is 0, or almost 0. endif wb = Azimuth_Path - prior ' The two arctangent answers are held in Azimuth_Path and prior. wb = ABS(wb) IF time < 720 THEN ' AM IF AMX > wb THEN AMX = wb AM = TIME ENDIF ELSE ' PM IF PMX > wb THEN PMX = wb PM = TIME ENDIF ENDIF NEXT TIME ' Next minute OSCCON = %01100000 ' Slow down micro to 4 MHz, numbers are crunched. return '********************* Get_Eyes: ' Low numbers mean bright eyes. IF dark THEN RETURN Eyes_after_dark: north = 0 south = 0 east = 0 west = 0 work1 = 8 'OSCCON = %01110000 ' Speed up micro to 8 MHz to crunch these numbers. FOR Smooth = 1 TO work1 HIGH alteye_a PAUSE 1 ' Charge eye capacitor. RCTIME alteye_a, 1, work ' Time to discharge capacitor through photoresistor eye. IF work = 0 THEN work = 1000 ' Timeout after 5000 count, 50000us or 50ms. north = north + work HIGH alteye_b PAUSE 1 RCTIME alteye_b, 1, work IF work = 0 THEN work = 1000 south = south + work HIGH azeye_a PAUSE 1 RCTIME azeye_a, 1, work IF work = 0 THEN work = 1000 east = east + work HIGH azeye_b PAUSE 1 RCTIME azeye_b, 1, work IF work = 0 THEN work = 1000 west = west + work NEXT smooth 'OSCCON = %01100000 ' Slow down micro to 4 MHz, numbers are crunched. north = north / work1 south = south / work1 east = east / work1 west = west / work1 if display then hserout [13,10] hserout ["Eye left ",dec east," down ",dec south,13,10] hserout ["Eye right ",dec west," up ",dec north,13,10] HSEROUT [" ---------------------------",13,10] hserout [" ",sdec (east - west)," ",sdec (south - north),13,10,10] endif if FIX = 0 then return ' No eye motor command if called from Set_Clock IF north < Too_Bright OR south < Too_Bright OR east < Too_Bright OR west < Too_Bright THEN stow.5 = 1 ' Eye failure tag ########### Altitude_Stow = yes Azimuth_Stow = yes ENDIF ' If one side of both eyes see sun or both sides of both eyes see sun2 then it must be sunny. IF (west < sun OR east < sun OR (west + east) < (sun2 * 2)) AND (north < sun OR south < sun OR (north + south) < (sun2 * 2)) THEN cloudy = no ELSE cloudy = yes wb = Altitude_Path + 1 ' One brad below 0 horizon. if wb > 68 THEN dark = yes ' If calculated sun position below horizon and cloudy then after sunset. RETURN ' Done with eyes because of clouds. ENDIF if (west <= east and azimuth_command = left) or (west >= east and azimuth_command = right) then azimuth_go = no IF ABS (west - east) > sun_gap THEN azimuth_go = yes IF west < east THEN ' Eyes command motors directions. azimuth_command = right ELSE azimuth_command = left endif if (north <= south and altitude_command = down) or (north >= south and altitude_command = up) then altitude_go = no if abs (north - south) > sun_gap THEN altitude_go = yes IF north < south THEN altitude_command = up ELSE altitude_command = down endif IF north < on_sun AND south < on_sun AND west < on_sun AND east < on_sun AND azimuth_go = no AND altitude_go = no THEN Focus = 1 ' If all eyes see bright sun and both motors off then the dish must be focused. ELSE ' Focus is required before position memory is recorded. Focus = 0 ' Dish could be pointed at sun with motors off but sun not bright enough for memory (focus on bright clouds). endif wb = altitude_path_sense - altitude_sensor ' Distance between dish pointing and solar position. IF Altitude_go = yes AND ABS wb > danger THEN ' If distance large then if commanded by the eye to move in the wrong direction then tracking error. IF (altitude_path_sense > altitude_sensor AND Altitude_command = down) OR (altitude_path_sense < altitude_sensor AND Altitude_command = up) THEN stow.7 = 1 ' Altitude eye/sensor/clock error. Check all three. ########### Azimuth_Stow = yes RETURN ENDIF ENDIF if sun_overhead then skip_solar_noon 'skip azimuth check after sun passes directly overhead. wb = azimuth_path_sense - azimuth_sensor IF azimuth_go = yes AND ABS wb > (danger */ z) THEN ' Distance between dish pointing and solar position, danger converted to azimuth sensor type data. IF (azimuth_path_sense > azimuth_sensor AND azimuth_command = left) OR (azimuth_path_sense < azimuth_sensor AND azimuth_command = right) THEN stow.6 = 1 ' Azimuth eye/sensor/clock error. Check all three. ########### altitude_stow = yes RETURN eNDIF ENDIF IF Focus THEN if altitude_sensor - k > 60 and altitude_sensor - k < 68 then sun_overhead = yes ' Azimuth position not accurate nor needed when sun is directly overhead goto skip_solar_noon ' in the tropics a few days per year when declination = latitude. endif IF solar_noon and (Azimuth_sensor = due_south or Azimuth_sensor = due_north) THEN ' If focused on sun directly south or north IF ABS(Time - 720) > 60 THEN ' Then if clock is not within 40 minutes stow = $0F ' Solar_noon clock/position error. ########### altitude_stow = yes ' Then clock or eye or sensor do not agree - stow. return ENDIF I2Cwrite datapin, clockpin, $D0, 1, [$00, $12]: Time = 720 ' Long term autonomy solar noon clock reset. solar_noon = 0 ' Only write one solar noon per day write_flag = 1 ' Enable memory write of Focus location. ENDIF skip_solar_noon: IF write_flag THEN ' Write Focus dish position and solar declination once every 4 minutes. ' Overwites older data if tracking bright sun. if time < 120 or time > 1317 then return ' Memory space 2 AM to 10 PM Pointer = ((Time / 4 ) - 30) * 3 ' EEPROM DATA locations 0 to 899 WRITE pointer, Azimuth_sensor, Altitude_sensor, declination write_flag = 0 if display then hserout [7] ' Beep computer indicating EEPROM data write ENDIF ENDIF return '********************* Get_Memory: if read_flag then return ' Read memory only once every four minutes. dat1 = 0 dat2 = 0 read_flag = 1 Pointer = ((Time / 4 ) - 30) * 3 ' Time 120 to 1317 converted to pointer 0 to 897 if pointer > 897 then no_memory ' time = 120 to 1317 memory space 2 AM to 10 PM READ pointer, Azimuth_Memory, Altitude_Memory, old_declination ' Read prior day focus position data at current time. wb = old_declination - declination ' Find distance between prior day solar declination and current solar declination. IF ABS WB > drift or Altitude_Memory = $FF THEN Search_back ' If declination difference is greater than the value drift then look at times before and after current time. GOTO Remember ' Else memory satisfied. Search_back: dat1 = dat1 + 1 IF dat1 > Search THEN no_memory ' If searched too far back then no memory. Pointer = ((Time / 4 ) - 30) * 3 - (3 * DAT1) ' Pointer 0 to 897 if pointer > 897 then no_memory ' Memory space 2 AM to 10 PM. READ pointer, AzPast, AltPast, old_declination ' Read prior day focus position data at earlier time. wb = old_declination - declination IF ABS WB > drift or altpast = $FF THEN Search_back ' If data too old then search further back. Search_forward: dat2 = dat2 + 1 IF dat2 > Search THEN no_memory ' If searched too far forward then no memory. Pointer = ((Time / 4 ) - 30) * 3 + (3 * DAT2) ' Pointer 0 to 897 if pointer > 897 then no_memory ' Memory space 2 AM to 10 PM. READ pointer, AzFuture, AltFuture, old_declination ' Read prior day focus position data at later time. wb = old_declination - declination IF ABS WB > drift or altfuture = $FF THEN Search_forward ' If data too old then search further forward. WB = azFuture - azPast Azimuth_Memory = (ABS WB * dat1)/(dat1+dat2) ' Calculate scale and offset to dead reckon between WB = altFuture - altPast Altitude_Memory = (ABS WB * dat1)/(dat1+dat2) ' earlier and later sun memory data. IF azPast > azFuture THEN Azimuth_Memory = azPast - Azimuth_Memory ELSE Azimuth_Memory = azPast + Azimuth_Memory ENDIF IF altPast > altFuture THEN Altitude_Memory = altPast - Altitude_Memory ELSE Altitude_Memory = altPast + Altitude_Memory ENDIF GOTO Remember no_memory: Azimuth_Memory = 0 Altitude_Memory = 0 Remember: if display then Pointer = ((Time / 4 )-30)*3 + (3 * DAT1) ' To display age via declination of old data. IF POINTER > 896 THEN RETURN read pointer,wb,wb,old_declination endif return '********************* Get_Path: ' N=days H=+/-hours*15 degrees Day of year Hours from solar noon ' D=23.45 degrees*sin(360*(284+N)/365 Declination ' ALT=arcsin(sinL*sinD+cosL*cosD*cosH) ' AZ=arcsin(cosD*sinH/cosALT) h = (time ** 11650) - 128 ' 0.17777 brads per minute*65536=11650 WORK1 = SIN Latitude work = SIN declination SIGNA = WORK1.15 ^ WORK.15 ' If one or the other but not both negative alt = (ABS WORK1 * ABS WORK) / 127 ' alt = ((SIN Latitude) * ABS(SIN declination)) / 127 IF SIGNA THEN ALT = -ALT ' then negate result. work = COS h signA = work.BIT15 ' If cos (h) negative work = (ABS WORK) * (COS declination) / 127 * (COS Latitude) / 127 ' COS declination and COS Latitude always positive. IF signA THEN work = -work ' then negate result. alt = (alt + work) work1 = (alt * alt) MIN 16129 ' There is no arcsine. Brad circles are 256 units. The radius 127. The Hypotenuse squared 16129. work = SQR(16129 - work1) ' X = SQR ( H * H - Y * Y ) same as Cosine = ( 16129 - Sine * Sine ). Angle = Arctangent Sine/Cosine Altitude_Path = work ATN alt ' Altitude brad angle = arctangent (Y/X) X ATN Y work = COS Altitude_Path az = ((COS declination) * (SIN h)) signA = work.BIT15 ^ az.BIT15 ' If one or the other but not both negative az = (ABS az / ABS work) MIN 127 work1 = az*az IF signA THEN az = -az ' then negate result. work = SQR(16129 - work1) ' X = SQR ( H * H - Y * Y ) Azimuth_Path = (work ATN az) + 128 ' Altitude brad angle = arctangent (Y/X) X ATN Y prior = (-work ATN az) + 128 ' X=SQR(H*H-Y*Y) has two solutions X and -X. 'prior' holds the second solution. The program flags the correct solution. return '********************* Set_Motors: IF sw1 = 0 AND sw2 = 0 THEN ' If both switches flipped then azimuth_go = no ' stop azimuth motor altitude_go = no ' stop altitude motor. ENDIF IF sw1 = 1 AND sw2 = 0 THEN ' If one switch flipped then altitude up and on. altitude_go = yes altitude_command = up azimuth_go = no ' Azimuth motor off. ENDIF IF sw1 = 0 AND sw2 = 1 THEN ' If the other switch flipped then altitude down and on. altitude_go = yes altitude_command = down azimuth_go = no ' Azimuth motor off. ENDIF Motor_Switch_Bypass: ' Use when keyboard controls motors. if azimuth_sensor <= minxy and azimuth_command = left then azimuth_go = no ' Limit azimuth left switch. Altitude drive has built in limit switches. if azimuth_sensor >= maxxy and azimuth_command = right then azimuth_go = no ' Limit azimuth right switch. IF azimuth_go = no AND altitude_go = NO AND azimuth_on = NO AND altitude_on = NO THEN warm = no ' Sleep HBridge goto No_Change else warm = yes ' Enable HBridge endif IF azimuth_on = yes AND (azimuth_command <> azimuth_spin or azimuth_go = no) THEN ' Stop before reverse. do ' reduce speed to zero in less than a second wb = speedaz speedaz = wb - 2 ' Slow azimuth if speedaz > wb then speedaz = 0 ' If azimuth speed crosses over from 0 to 255 then azimuth speed = 0 hpwm 1, SPEEDAZ, freq ' Set PMW to HBridge pause 8 loop until speedaz = 0 ' Exit when motor speed is zero low azmotor_a ' Brake azimuth motor pause 1 low azmotor_b azimuth_on = no pause 8 endif IF altitude_on = yes AND (altitude_command <> altitude_spin or altitude_go = no) THEN ' Stop before reverse. do ' Reduce speed to zero in less than a second. wb = speedalt speedalt = wb - 2 ' Slow altitude. if speedalt > wb then speedalt = 0 ' If altitude speed crosses over from 0 to 255 then altitude speed = 0 hpwm 2,speedalt, freq ' Set PMW to HBridge pause 8 loop until speedalt = 0 ' Exit when motor speed is zero low altmotor_a ' Brake altitude pause 1 low altmotor_b altitude_on = no pause 8 endif Azimuth: if speedaz = maxaz or azimuth_go = no then altitude ' If maximum Azimuth speed or Azimuth motor off then no need to change speed. if speedaz = 0 then speedaz = startaz ' If zero speed then starting speed wb = speedaz + rampaz ' Increment flag speed. if wb > maxaz or wb < speedaz then ' If flag speed > maximum speed or flag speed crossed over from 255 to 0 speedaz = maxaz ' then set speed to maximum speed else ' else increment speed to flag speed. speedaz = wb endif if azimuth_on <> azimuth_go then ' If off and and need to go on then set direction first. if azimuth_command = right then high azmotor_a ' Both A and B were off from previous brake if azimuth_command = left then high azmotor_b pause 1 endif hpwm 1, SPEEDAZ, freq 'set pwm ' Set azimuth speed. Altitude: if speedAlt = maxAlt or Altitude_go = no then good ' If maximum Altitude speed or Altitude motor off then no need to change speed. if speedAlt = 0 then speedAlt = startAlt ' If zero speed then starting speed wb = speedAlt + rampAlt ' Increment flag speed. if wb > maxAlt or wb < speedAlt then ' If flag speed > maximum speed or flag speed crossed over from 255 to 0 speedAlt = maxAlt ' then set speed to maximum speed else ' else increment speed to flag speed. speedAlt = wb endif if altitude_on <> altitude_go then ' If off and and need to go on then set direction first. if altitude_command = up then high altmotor_a ' Both A and B were off from previous brake if altitude_command = down then high altmotor_b pause 1 endif hpwm 2,speedalt, freq 'set pwm good: IF azimuth_go AND azimuth_on = no THEN ' If azimuth motor goes from off to on then Azimuth_Motor = 1 ' start counting azimuth motor on time Azimuth_Move = Azimuth_sensor ' store current azimuth location. ENDIF IF azimuth_go = no THEN Azimuth_Motor = 0 ' If azimuth motor off then stop counting on time. IF altitude_go AND altitude_on = no THEN ' If altitude motor goes from off to on then Altitude_Motor = 1 ' start counting altitude motor on time Altitude_Move = Altitude_sensor ' store current altitude location. ENDIF IF altitude_go = no THEN Altitude_Motor = 0 ' If altitude motor off then stop counting on time. azimuth_on = azimuth_go ' Motors followed commands. azimuth_spin = azimuth_command ' Motors status now reflect those commands. altitude_on = altitude_go altitude_spin = altitude_command No_Change: IF azimuth_go = no AND altitude_go = NO THEN warm = no ' Sleep HBridge if display then hserout [10,13,"Motors "] if azimuth_on then if azimuth_spin = left then hserout [ " left -"] else hserout [" right "] endif else hserout [" "] endif hserout [dec speedaz] if altitude_on then if altitude_spin = down then hserout [" down -"] else hserout [" up "] endif else hserout [" "] endif hserout [dec speedalt,10,13] hserout [10,13] endif return '********************* Set_Clock: ' Set clock to standard time. Bright sun solar tracking will update clock to solar noon time. FIX = 0 ' Stop eyes from controlling motors. TEST = 0 ' Clear menu choice. 'display = 1 ' Enable serial port azimuth_go = no ' Stop azimuth motor altitude_go = no ' Stop altitude motor GOSUB Motor_Switch_Bypass ' Stop switches from controlling motors KEYPAD: 'hserout [12] gosub get_time hserout [10,13] hserout [ " Clock set ",10,13 ] hserout [ " Drive motors and read sensors ",10,13 ] hserout [ " Memory Read or erase ",13,10] hserout [ " Install limits ",13,10] HSEROUT [ " Run Program ",13,10,10 ] hserin [wb] hserout [12] if wb = "C" then Clock_set IF WB = "D" THEN Drive if wb = "M" then Read_Write if wb = "I" then Install IF WB = "R" THEN TOP WB = 0 GOTO KEYPAD Clock_set: GOSUB GET_TIME hserout [ "0 second ",hex sec,13,10] hserout [ "1 minute ",hex minute,13,10] hserout [ "2 hour ",hex hour,13,10] hserout [ "3 day ",hex day,13,10] hserout [ "4 date ",hex date,13,10] hserout [ "5 month ",hex month,13,10] hserout [ "6 year ",hex year,13,10] hserout [ "7 reset ",13,10] hserout [ "8 exit ",13,10] hserin [dec1 wb] IF wb = 7 THEN reset if wb > 7 then keypad hserout [10,13,DEC wb," value? "] hserin [hex2 wb1] HSEROUT [13,10,10] I2Cwrite datapin, clockpin, $d0, wb, [wb1] ' Set Clock goto clock_set RESET: FOR smooth = 7 TO 0 ' Clear Clock DS1307 clock reset and start crystal I2Cwrite datapin, clockpin, $d0, smooth, [0] ' Run when backup battery replacement NEXT GOTO clock_set Drive: HSEROUT [12] GOSUB GET_SENSORS GOSUB GET_TIME hserout [13,10," Azimuth Altitude",10,13,10] GOSUB Eyes_after_dark hserout ["Sensor ",dec Azimuth_sensor, " ",dec Altitude_sensor,10,13] GOSUB Motor_Switch_Bypass HSEROUT [13,10] hserout ["Left Right XYstop Up Down Zstop",13,10,10] HSEROUT ["Exit"] hserin [wb] if wb = "E" then set_clock if wb = "L" then azimuth_command = left azimuth_go = yes endif if wb = "R" then azimuth_command = right azimuth_go = yes endif if wb = "X" then azimuth_go = no if wb = "U" then altitude_command = up altitude_go = yes endif if wb = "D" then altitude_command = DOWN altitude_go = yes endif if wb = "Z" then altitude_go = no goto drive Read_Write: ' EEPROM DATA locations: 0 to 899 tracking memory, 900 to 999 stow data, 1000 to 1016 sensor calibration, 1017 to 1024 available. HSErout [13,10,"Read Write Exit",13,10,10] hserin [wb] if wb = "E" then keypad if wb = "R" then reads if wb = "W" then writes goto read_write Reads: hserout [10,13,10," end? = 0 to see Stow data",13,10] hserout [10,13,"begin? "] hserin [dec work1]: if work1 > 899 then work1 = 899 hserout [10,13," end? "] hserin [dec work2]: if work2 = 0 then stow_data if work2 > 899 then work2 = 899 WORK1 = WORK1/3*3 hserout [10,13,dec work1," to ",dec work2,10,13] FOR POINTER = WORK1 TO WORK2 STEP 3 READ pointer, Azimuth_Memory, Altitude_Memory, old_declination WORK4 = (POINTER / 3 + 30) * 4 ' Converts memory location to time value. WB1 = (Azimuth_Memory - DUE_NORTH) */ f ' Converts azimuth sensor data to brads. WB2 = ALTITUDE_Memory - K ' Converts altitude sensor data to brads. Hserout [dec pointer,32,dec work4," ",dec Azimuth_Memory," ",dec Altitude_Memory," ",DEC old_declination," ", DEC WB1," ",DEC WB2,13,10] next pointer hserout ["pointer Sensor Dec Brad",13,10] hserout ["ptr time az alt az alt",13,10] Azimuth_Memory = 0 Altitude_Memory = 0 goto read_write stow_data HSEROUT [12] for pointer = 900 to 996 step 4 read pointer, wb, stow, Azimuth_sensor, Altitude_sensor ' WB is stow time/10. IF WB = $FF THEN NXT ' No print if altitude blank with $FF WB1 = (Azimuth_SENSOR - DUE_NORTH) */ f ' Converts azimuth sensor data to brads. WB2 = ALTITUDE_SENSOR - K ' Converts altitude sensor data to brads. hserout [dec pointer,32,dec4 wb*10,32,32,bin8 stow," ", DEC Azimuth_sensor,32, DEC Altitude_sensor," "] hserout [DEC WB1,32,DEC WB2,13,10] NXT: next pointer hserout [10,13,"pointer tag Sensor Brad",13,10] hserout ["ptr time stow az alt az alt",13,10,10] hserout [ "00000001 Azimuth Sensor",13,10] hserout [ "00000010 Altitude Sensor",13,10] hserout [ "00000100 Clock time",13,10] hserout [ "00001000 Azimuth Motor",13,10] hserout [ "00010000 Altitude Motor",13,10] hserout [ "00100000 Eyes",13,10] hserout [ "01000000 Azimuth sensor/eye/time",13,10] hserout [ "10000000 Altitude sensor/eye/time",13,10] hserout [ "00001111 Solar noon Clock",13,10] hserout [ "11110000 Water pressure",13,10] hserout [ "11111111 Wind",13,10] goto read_write Writes: ' Clears EEPROM segment and writes $FF HSEROUT [10,13,"Erase Tracking data 0 to 899"] HSEROUT [10,13," Stow data 900 to 999",10,13] hserout [10,"begin location? "] hserin [dec work1]: if work1 > 999 then work1 = 999 hserout [10,13," end location? "] hserin [dec work2]: if work2 > 999 then work2 = 999 hserout [10,13,dec work1," to ",dec work2,10,13," Yes? "] hserin [wb] if wb <> "Y" then goto read_write wb = $FF FOR POINTER = WORK1 TO WORK2 write pointer,wb hserout [7] ' Beep computer indicating EEPROM data write HSEROUT [DEC POINTER,32] next pointer goto read_write Install: read 1000, WORD lat, due_north, due_south, due_up, minxy, maxxy, minz, maxz, dummy, azmotor, altmotor, azsensor, altsensor, azeye, alteye HSEROUT [12] HSEROUT [" Latitude degrees times 10",10,13," Example: Seattle = 475",13,10," Other values are dish sensor data",13,10,10] ' Negative Latitude below equator. HSEROUT ["0 Latitude ",Sdec lat,13,10] HSEROUT ["2 Due North ",dec due_north,13,10] ' Use Polaris star to find true north. HSEROUT ["3 Due South ",dec due_south,13,10] HSEROUT ["4 Due Up ",dec due_up,13,10] HSEROUT ["5 Minimum XY ",dec minxy,13,10] HSEROUT ["6 Maximum XY ",dec maxxy,13,10] HSEROUT ["7 Minumum Z ",dec minz ,13,10] HSEROUT ["8 Maximum Z ",dec maxz ,13,10] HSEROUT ["9 Exit ",13,10] HSEROUT [10," Reverse direction 1 or 0", 13,10,10] HSEROUT ["A Azimuth Motor ",DEC AZMOTOR, 13,10] HSEROUT ["B Altitude Motor ",DEC ALTMOTOR, 13,10] HSEROUT ["C Azimuth Sensor ",DEC AZSENSOR, 13,10] HSEROUT ["D Altitude Sensor ",DEC ALTSENSOR,13,10] hserout ["E Azimuth Eye ",dec azeye,13,10] hserout ["F Altitude Eye ",Dec alteye,13,10] hserin [hex1 wb] IF WB > $0F THEN INSTALL HSEROUT [ 10,13,10,hex WB," = "] if wb = 9 then keypad IF WB = 1 THEN INSTALL hserin [dec3 work] wb1 = work if wb = 0 then write 1000, word work else write 1000 + wb, wb1 ' Write to EEPROM install values. endif hserout [7] ' Beep computer indicating EEPROM data write goto install goto ending