' DUAL-AXIS SOLAR TRACKING ' ' Version 17 April, 2013 ' ' New tracker circuits, version 2013 not downward compatible with prior trackers. Now with Southern latitudes routines. ' '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ' ' Current version http://www.harbornet.com/sunflower/pvdish.html ' Serial port with notebook computer. '**************************************************************** '* Name : sunrun.pbp * '* Author : Sunflower fox@harbornet.com * '* 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. ' NOAA Solar Calculator http://www.esrl.noaa.gov/gmd/grad/solcalc/ ' 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 "18F4620.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 and .bal files 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 and high SLEW with board jumper pads. ' ' MMA7361L analog accelerometer(need two per dish) http://www.pololu.com/catalog/product/1251 ' Supply can be 12 VDC -- wire from micro power switch so that sensor is also switched. Other wires are X axis, Y axis, and ground ' ' Pololu 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). ' ' TEPT5600 Vishay visible light photo transistors (need four per dish) ' http://www.vishay.com/photo-detectors/list/product-84768/ ' Pull pins high with 500+ ohm resistors, connect transistor collectors to pins and transistor emitters to ground. ' ' The two eyes are constructed under two 30 mm glass balls positioned with the ball focus over razor-like dividers ' separating two sensors (set back 3 inches) in each eye. One eye is for azimuth and the other eye is for altitude. ' Sensor sensitivity is something like +/-0.0005 degrees. ' ' DS1307 clock, I2C pull up 4.7k, 5 VDC power supply through BAT85 Schottky Diode (>4.5 VDC). Battery lithium coin 3V 16mm CR1620, ' battery holder, crystal 32.768kHz 12.5PF, 8 pin IC socket ' ' D9 plug pins on a RS232 serial cable may have colors: 2-brown, 3-red, 5-yellow, and loop back to laptop 7-blue, 8-purple. ' ' ' Physical 18F4620 pin connections: All motor connections via Dual MC33926 Motor Driver HBridge (max hot sustained 3 amps per motor). ' ' 1 - Push button ground to reset ' 2 - LED on carrier board (pin available with LED jumper removed) ' 3 - Available analog pin ' 4 - AN2 Eye Azimuth right pulled up 500 Ohm ' 5 - AN3 Eye Azimuth left pulled up 500 Ohm ' 6 - Available digital pin ' 7 - AN4 Eye Altitude up pulled up 500 Ohm ' 8 - AN5 Eye Altitude down pulled up 500 Ohm ' 9 - AN6 Altitude Motor Amps (M2FB) ' 10 - Olimex push button on board pulled high 10k ' 11 - VDD connected on board Micro <12 VDC and motor controller <28 VDC may have seperate power supply. ' 12 - VSS connected on board Micro and motor controller must have common ground (after power regulators, in circuit). ' 13 - Available digital pin ' 14 - Available digital pin ' 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, 5 VDC power supply through BAT85 Schottky Diode ' 19 - Altitude motor (M2 IN1)+ ' 20 - Altitude motor (M2 IN2)- ' 21 - Azimuth motor (M1 IN1)+ ' 22 - Azimuth motor (M1 IN2)- ' 23 - DS1307 clock (I2C SDA pin), pull up 4.7k ' 24 - Pin ~ 230 ohm resistor ~ Wind wire + 0.1mf capacitor ~ ground. Ground wind wire to trigger stow. Push button useful. ' 25 - Connect to MX232 RX ' 26 - Connect to MX232 TX ' 27 - Switch 1 pulled high 10k resistor ' 28 - Switch 2 pulled high 10k resistor ' 29 - Switch 3 pulled high 10k resistor ' 30 - Switch 4 pulled high 10k resistor ' 31 - VSS connected on board ' 32 - VDD connected on board ' 33 - AN12 Accelerometer Altitude Y ' 34 - AN10 Accelerometer Azimuth Y ' 35 - AN8 Azimuth Motor Amps (M1FB) ' 36 - AN9 Accelerometer Azimuth X ' 37 - AN11 Accelerometer Altitude X ' 38 - Available pin (also used for programing) ' 39 - Available pin (also used for programing) ' 40 - Available pin (also used for programing) ' ' DEFINE LOADER_USED 1 ' Protect RS232 Bootloader Space Otherwise micro will need reprogramming with conventional programmer on pins 38,39,40,1. DEFINE OSC 4 ' Tell program 4 MHz timing DEFINE PULSIN_MAX 1000 ' TIMEOUT LIMIT FOR RCTIME and PULSIN COUNT 10us units @ 4MHz 10ms timeout DEFINE HSER_CLROERR ' Automatic clear communications error DEFINE ADC_BITS 10 ' Set number of bits in result DEFINE ADC_SAMPLEUS 50 ' Set sampling time in uS DEFINE ADC_CLOCK 3 ' Set Frc clock for ADC Module ADCON1 = %00000000 ' Make AN0-AN12 analog OSCCON = %01100000 ' Internal oscillator speed 4 MHz WDTCON = 1 ' Enable watchdog LED VAR PORTA.0 'AN0 analog available with jumper removed. ' 'PORTA.1 AN1 Available analog 'PORTA.2 AN2 PORTA.2 & PORTA.3 reversible in software -- Azimuth eye 'PORTA.3 AN3 PORTA.2 & PORTA.3 reversible in software -- Azimuth eye ' PORTA.4 Available digital 'PORTA.5 AN4 PORTA.5 & PORTE.0 reversible in software -- Altitude eye 'PORTE.0 AN5 PORTA.5 & PORTE.0 reversible in software -- Altitude eye 'PORTE.1 AN6 Altitude Motor Amps 'PORTB.2 AN8 Azimuth Motor Amps 'PORTB.3 AN9 PORTB.3 & PORTB.1 reversible in software -- Azimuth accelerometer 'PORTB.1 AN10 PORTB.3 & PORTB.1 reversible in software -- Azimuth accelerometer 'PORTB.4 AN11 PORTB.4 & PORTB.0 reversible in software -- Altitude accelerometer 'PORTB.0 AN12 PORTB.4 & PORTB.0 reversible in software -- Altitude accelerometer ' PORTD.0 PORTD.0 & PORTD.1 reversible in software -- Azimuth motor drive signal ' PORTD.1 PORTD.0 & PORTD.1 reversible in software -- Azimuth motor drive signal ' PORTD.2 PORTD.2 & PORTD.3 reversible in software -- Altitude motor drive signal ' PORTD.3 PORTD.2 & PORTD.3 reversible in software -- Altitude motor drive signal sw1 var PORTD.4 'Switch1 -- Altitude motor control sw2 var PORTD.5 'Switch2 -- Altitude motor control sw3 var PORTD.6 'Switch3 -- Azimuth motor control sw4 var PORTD.7 'Switch4 -- Azimuth motor control 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 Wind_pin var PORTC.5 ' Wind sensor grounds charged capacitor at 25 mph. ' PORTC.6 TX HSEROUT RS232 Connect to MX232 RX ' PORTC.7 RX HSERIN RS232 Connect to MX232 TX time VAR Word ' 1440 minutes per day old_time var word ' Previous minute Old_seconds var word ' off time in clock ram New_seconds var word ' on time 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_X var byte azSensor_Y var byte altSensor_X var byte altSensor_Y var byte azEye_Left var byte azEye_Right var byte altEye_Down var byte altEye_Up var byte flag VAR word SIGNa VAR FLAG.0 need_sunrise 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 Down_Limit var morebits.2 Up_Limit var morebits.3 Flag_overhead var morebits.4 Up_Limit_Switch var moreBits.5 Down_Limit_Switch var morebits.6 Left_Limit_Switch var morebits.7 Bits var byte Right_Limit_Switch var bits.0 Left_Limit var bits.1 Right_Limit var bits.2 Motor_Power var bits.3 Az_Fail var bits.4 Alt_Fail var bits.5 Print var bits.6 No_Limits var bits.7 Bats var byte s1 var bats.0 s2 var bats.1 s3 var bats.2 s4 var bats.3 HoldAz var bats.4 HoldAlt var bats.5 Hot_Install var bats.6 Skip_amps var bats.7 Delta var byte ' overlap exchange value +/- Top var byte ' Top overlap value Bottom var byte ' bottom overlap value text var byte ' Menu character pointer VAR word second var byte previous_second var byte smooth var byte k var byte ' Add altitude constant derived from sensor installation kk var byte ' Add azimuth constant derived from sensor installation z var word ' Scale constant derived from sensor installation f var word ' Scale constant derived from sensor installation South_North var byte ' south-north crossing 0 does not matter ~ 44-250=50 in byte due_north_c var byte ' corrected due_north due_south_c var byte ' corrected due_south Azimuth_Sensor_Brads VAR BYTE ' sensor brads Altitude_Sensor_Brads VAR BYTE ' sensor brads Azimuth_Memory VAR BYTE Altitude_Memory VAR BYTE Azimuth_Memory_Brads VAR BYTE Altitude_Memory_Brads 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 ' Path data converted to sensor data Altitude_Path_sense VAR BYTE Azimuth_Destination VAR BYTE ' If Azimuth_Destination = Azimuth_sensor then azimuth motor stop Altitude_Destination VAR BYTE ' If Altitude_Destination = Altitude_sensor then Altitude motor stop 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 raw sensor value of Polaris star. due_south var byte ' Azimuth raw sensor value of due south due_up var byte ' Altitude raw 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 Old_SpeedAz var byte ' Used to ramp speed to new SpeedAz Old_SpeedAlt var byte ' Used to ramp speed to new SpeedAlt AzAmps var word ' ma used by azimuth motor AltAmps var word ' ma used by altitude motor 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 X var work1 Y var work2 Xw var work3 Yw var work4 WorkByte var byte WB VAR BYTE WB1 VAR BYTE WB2 VAR BYTE dummy var byte Xb var WB1 Yb var WB2 travel var wb azWB var WB1 altWB var WB2 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 dat2 VAR BYTE ' Memory dead reckoning variables. dat1 VAR BYTE azPast VAR BYTE altPast VAR BYTE azFuture VAR BYTE altFuture VAR BYTE sunny var word 'Sun detect level from one eye side sunny2 var word 'Sun detect level from both eye sides 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 edge con 40 ' edge of cloud sun CON 750 ' Sunny if one eye sees sun. sun2 CON 850 ' Sunny if both eyes see bright. sun_Gap CON 100 ' If eye difference beyond gap then go Azimuth_Gap CON 2 ' Accelerometer +/- error. Altitude_Gap CON 2 ' Accelerometer +/- error. ON_SUN CON 450 ' If both eyes < ON SUN then focused in clear sun, record position to memory AzAmp_Pin con 8 ' Azimuth motor amp data AN8 pin AltAmp_Pin con 6 ' Altitude motor amp data AN6 pin Too_Bright CON 2 ' Eye failure. danger CON 20 ' 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. rampAz CON 2 ' Unit of azimuth motor speed increase per second rampAlt CON 2 ' Unit of altitude motor speed increase per second startAz CON 174 ' Minimum azimuth motor start speed startAlt CON 164 ' Minimum altitude motor start speed maxAz CON 250 ' 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 dawn_up con 96 ' Brads up dark park waiting for dawn 128 due_up 64 horizon dawn_over con 00 ' Brads counterclockwise park waiting for dawn brads_overhead con 120 ' Overhead near sun value, 128 directly up ' Top of the program PAUSE 10 CLEAR ' Must clear all varibles on startup PAUSE 1 PORTA=0 PORTB=0 PORTC=0 PORTD=0 PORTE=0 TRISA = %11111111 TRISB = %11111111 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 TRISE = %00000111 if RCON.1 = 0 THEN ' If power cycled and not Reset button. GOSUB Get_Time GOSUB POWER_CYCLE RCON.1 = 1 endif reboot = 1 ' Manual drive and power reset flags high wind_pin ' Charge capacitor connected to wind detector. pauseus 100 INPUT wind_pin pause 1 gosub register ' Read INSTALL values s1=sw1:s2=sw2:s3=sw3:s4=sw4 ' remember switches IF sw1 = 0 THEN No_Stow = yes ' If switch flipped before power up then dish will not stow for error or wind. IF SW2 = 0 THEN display = yes ' If switch flipped before power up then serial display enabled. Program loops 2 seconds slower. if sw3 = 0 then skip_amps = yes ' If switch flipped before power up then motor power fail check disabled. Use for motors that draw <10 ma. if sw1 = 1 and sw2 = 1 and sw3 = 0 and sw4 = 0 then ' If both azimuth switches and not altitude switches then power on and wait for for smooth = 1 to 50 ' more than 25 seconds to enable sensor Install registration with just switches & w/o laptop. pause 500 ' Delay is for protection. if sw1=0 or sw2=0 or sw3=1 or sw4=1 then not_hot next smooth Hot_Install = yes goto install endif Not_Hot: ' Switches pulled up default at power start-up (OFF flipped toward box edge). ' ' Flipped ON towards box center before reset/power-on enables safety stow, serial display port RS232, and sensor registration. ' ' sw1 = 0 ON Disable Stow. ' sw2 = 0 ON Eable display. ' Both altitude 1,2 OFF and both azimuth 3,4 ON enable Install and Registration of due up, due north, and due south. ' ' Two seconds after reset/power-on -- ' 1,2 off = altitude automatic. 1 on = up. 2 on = down. 1,2 on = motor off. ' 3,4 off = azimuth automatic. 3 on = right. 4 on = left. 3,4 on = motor off. ' ' Tripping wind sensor causes rapid stow and also rapid ramp-up motor speed from switches. ' Motor ramp-up speed is also 3 times faster when Display is disabled. ' ' Display shows raw eye data, motor direction and speed, motor milliamps during acceleration and after maxiumum steady speed. ' ' Path Brads - Memory - Sensor are in Brads (binary radians) 0-255 = 256 in a circle. ' 0 = north, 64 = east, 128 = south, 192 = west, 64 = horizon, 128 = up. Solar declination is in Brads. ' South of Equator: 0 = south, 128 = north ' ' Path Sense - Memory - Sensor, and Destination are in corrected sensor type data, same as data recorded to EEPROM memory. ' Install and Drive functions show raw sensor type data used in Due_up, Due_north, and Due_south registration. ' ' The next line shows climatic conditions and command. Reason for Stow will be displayed with or without stow enabled. Flipped switches will be displayed. ' ' (Press CAPS LOCK) Menu choices are the first capital letter or number in the list. ' ' Initial Installation checks and corrects motor spin direction. Flip SW1 on to confirm motor spin up and SW3 on to confirm motor spin right. ' Senors should show increases up and right. Eyes should show low numbers for bright side. ' Use Install function to reverse these components as necessary (or reverse wires). ' ' After correcting spin and direction then register due up, due north, and due south using raw sensor data. ' ' Install maxXY 250, minXY 5 (both near senor limits), maxZ 130, minZ 64 (both at actuary limit switches). ' Due_up Due_north and Due_south uses raw sensor type data found in the Drive function. This data can also be registered by flipping switches (see below). ' Be careful not to use more than one switch at a time during switch Install function to avoid accidental raw data registration. ' ' *** Switch operation *** ' ' Power switch off flip towards box edge. Power for micro <12 VDC and for motors <28 VDC may be separate sources and have sepatate switches. ' Both power sources have common circuit ground so use lower battery near ground for micro if 2 batteries in series are used for motors. ' ' Use micro power switch for on/off before connecting / disconnecting power to prevent corruption of time clock data. ' Motors will be off so motor power switch may remain on. ' ' Press push button to reset power anytime. Also press reset then, within one second, click Flash_Write Tiny Bootloader to load program updates. ' ' If both atitude switches 1,2 and azimuth switches 3,4 in off position (towards box edge) before power-on then default auto solar tracking running after reset or after power-on. ' If altitude switch 2 on (towards box center) before reset/power-on then serial port enabled for communications. Program loop runs 2 seconds slower. ' If atitude switch 1 on before reset/power-on then the dish operates without error stow and wind stow functions. Reset/Power-on requires 2 seconds. ' If switch 3 on before power on then motor amp fail detect is disabled. ' ' If both atitude switches 1,2 are flipped to off after power-on then altitude motor runs on automatic. ' If one or the other switch is flipped on after power-on then altitude motor goes up or down. ' If both atitude switches are flipped on after power-on then atitude motor remains off. ' ' If both azimuth switches 3,4 are flipped to off after power-on then azimuth motor runs on automatic. (Power-on requires 2 seconds.) ' If one or the other switch is flipped after power-on then azimuth motor goes left or right. ' If both azimuth switches are flipped to on after power-on then azimuth motor remains off. ' ' If both altitude switches 1,2 OFF and both azimuth switches 3,4 ON before reset/power-on to enable Install due_up, due_north, due_south with or without laptop. ' Must wait more than 25 seconds after power on without flipping switches to enable sensor registration with switches. ' This wait period is for some protection against accidental registration of sensors with switches. ' ' During Install switch 1,2,3,4 off. Move dish position with one switch on at a time. ' When ready to register due_up (dish is pointing due up) flip 3 and 4 on then flip 1 on (up switch), wait one second, then flip switches 1,2,3,4 off. ' ' When ready to register due_north (dish is pointing due north) flip 1 and 2 on then flip 4 on (left switch), wait one second, then flip switches 1,2,3,4 off. ' ' When ready to register due_south (dish is pointing due south) flip 1 and 2 on then flip 3 on (right switch), wait one second, then flip switches 1,2,3,4 off. ' ' Reverse north and south when located south of the equator -- switch 4 for due south and switch 3 for due north ' ' When finished with Install then press reset button. ' ' After power-on or after reset button then the configuration switches control motors ' so after display communications and/or no-stow functions have been configured on power-on then ' flip configuration switches to default position (off towards box edge) 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. ' ' If power cycled less than 4 seconds then stow occurs (power cycled longer than 14 seconds clears all stows). This enables stow of field of dishes with power cycle. ' Reset does not cause stow. Press reset to clear all stow commands. Press Reset to clear stow after loading new programs. ' ' Accelerometers increase number rotating dish right or up. Dark side of eye is larger number. See Helm: for south of the Equator. ' ' South of Equator routines search "If Latitude.7" means if latitude is negative. Azimuth accelerometer increase number rotating dish left. ' Azimuth tracking mirror image of Nothern latitudes. North/South reversed. Main: 'XXXXXXXXXXXXXXXXXXXXXX MAIN LOOP sunny = sun sunny2 = sun2 FIX = 1 no_limits = no IF wind_pin = 0 then ' If wind detector discharged capacitor then stow stow = $FF altitude_stow = yes azimuth_stow = yes endif high wind_pin ' Charge capacitor connected to wind detector. pauseus 100 INPUT wind_pin old_time = time ' Clock ticking time is old 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. print = yes GOSUB Get_Time ' Number of minutes since midnight, and number of days since January 1 gosub check_time Azimuth_Path_sense = due_north_c + (Azimuth_Path */ z) ' Converts calculated Path Brads to specific dish sensor type data. if tropic_summer and Azimuth_Path > 190 then Azimuth_Path_sense = due_north_c - ((256-Azimuth_Path) */ z) ' If tropical sun azimuth path crosses north 0 to 255 tracking left towards west. Altitude_Path_sense = Altitude_Path ' Converts calculated Path Brads to specific dish sensor type data. GOSUB Get_Eyes ' Eyes +/- 0.0005 degrees. Creates motor commands +/- 0.05 degrees. 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 in clouds. dwell = 8 ' Wait 8 minutes if dish was previously focused. ELSE ' else dwell = 1 ' Wait 1 minutes if previously tracking while sunny. endif GOSUB set_Motors print = no gosub check_motors goto run2 ' Print and loop Park: Altitude_destination = dawn_up ' Horizon constant Azimuth_destination = dawn - (dawn_over */ z) ' Sunrise plus move left 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 if Azimuth_sensor < 128 and Azimuth_destination > top then Azimuth_destination = Azimuth_destination - (256 */ z) ' - Circle sensor units if Azimuth_sensor > 127 and Azimuth_destination < bottom then Azimuth_destination = Azimuth_destination + (256 */ z) ' + Circle sensor units 'If dish azimuth sensor is low and destination is high and sensor overlaps within high and low then circle destination from high to low. Tropics 'If dish azimuth sensor is high and destination is low and sensor overlaps within high and low then circle destination from low to high. Tropics if Altitude_path > Brads_overhead then Azimuth_destination = Azimuth_sensor ' Stop azimuth motor due to uncertain memory azimuth from overhead sun in tropics. Check_Stow: if No_Stow = Yes or stow = no then skip_stow IF Altitude_Stow THEN speedalt = maxalt ' maxalt set_motor maximum speed Altitude_destination = 128 ' Point dish due up wb = Latitude - declination wb = abs wb if wb < 4 THEN Altitude_destination = 122 ' Near overhead tropic sun. Angle off axis * 2 = image location off receiver 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 speedaz = maxaz Azimuth_destination = due_north_c ' Point dish north if declination.7 = latitude.7 and (abs declination) >= (abs latitude) then azimuth_destination = due_south_c 'tropic summer stow away from sun IF Altitude_Stow = no THEN Altitude_destination = Altitude_sensor ' Altitude_destination = Altitude_sensor will tell Helm to stop altitude motor. ENDIF ENDIF IF 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 if pointer = 992 then pointer = 900 ' over write old data wb = time/10 ' Byte size of time/10 WRITE pointer, wb, stow, Azimuth_sensor, Altitude_sensor, $ff ' Record time, cause, dish position stow_write = 1 ' write eeprom only once. 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 print = yes 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 wb = abs wb ' Azimuth... IF 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 northern latitudes if latitude.7 then azimuth_command = left ' Set direction southern latitudes ELSE azimuth_command = left ' When south of the Equator swap right for left and set azimuth accelerometer increase right to left. if latitude.7 then azimuth_command = right endif ' Also Path(azimuth) = 128-Path (byte). Done. Reverse Due_North with Due_South during Install Function. Done if azimuth_command <> azimuth_spin then azimuth_go = no ' prevent hunting. skipper: wb = Altitude_sensor - Altitude_destination wb = abs wb ' Altitude... IF 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 if altitude_command <> altitude_spin then altitude_go = no ' prevent hunting. skippy: GOSUB Set_Motors print = no Gosub check_motors goto run2 ' Print and loop. run2: print = yes if display and print then text = 0 ' Holds menu choice. hserout [" Declination",10,13] 'hserout [" Azimuth Altitude Dec",10,13,10] hserout ["Path brads ",dec Azimuth_Path, " ",dec Altitude_Path," ",Sdec declination,10,13] hserout ["Memory ",dec Azimuth_Memory_brads, " ",dec Altitude_Memory_brads," ",Sdec old_declination,10,13] hserout ["Sensor ",dec Azimuth_sensor_brads, " ",dec Altitude_sensor_brads,10,13,10] hserout ["Path sense ",dec Azimuth_Path_sense, " ",dec Altitude_Path_sense," ",10,13] hserout ["Memory ",dec Azimuth_Memory, " ",dec Altitude_Memory," ",10,13] hserout ["Sensor ",dec Azimuth_sensor, " ",dec Altitude_sensor,10,13] hserout [" "] if sw1=0 then hserout ["SW1 "] else hserout [" "] endif if sw2=0 then hserout ["SW2 "] else hserout [" "] endif if sw3=0 then hserout ["SW3 "] else hserout [" "] endif if sw4=0 then hserout ["SW4 "] else hserout [" "] endif hserout [10,13,"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 "] ' text choices. HSERIN 1,timeout,[text] ' Hold menu choice -- capital letter or number of displayed choices pause 10 iF text = "E" THEN SET_CLOCK Timeout: endif IF azimuth_on = no AND altitude_on = no AND DISPLAY = NO and no_stow = no AND sw1 and sw2 and sw3 and sw4 THEN IF dark or stow THEN SLEEP 10 ENDIF pause 1000 IF DISPLAY THEN PAUSE 2000 ' program runs 3 times slower with display, motors ramp speed 3 times slower. goto main ' Big Loop ending: ' Never happens unless 'ending' is called. end '********************* register: ' Azimuth sensor starts >0 due north (>0 due west in equatorial latitudes) then increases clockwise (from left to right) and ends <255 north by nortwest. ' Altitude sensor starts at 64 horizon and ends 128 vertical. ' Binary radians 256 Brads = 360 Degrees ' READ dish specific values entered during installation using the command Install Limits. ' Use Polaris or Google maps or shadow of plumb bob at solar noon to determine true north and south. ' lat is site latitude in degrees times 10. ' due_north, due_south, due_up, calibrates dish position sensors with raw sensor data. This data can be registered using tracker switches without laptop. ' Minxy, maxxy, minz, maxz, (software travel limits with corrected sensor data), azMotor, altMotor, azSensor, altSensor, azEye, altEye, ' (stored flags that reverse wires on pins as needed). All this can be configured before installation. ' ' Solar Path algorithm output -- ' ' Horizontal is 0 brads. ' Vertical is 64 brads. ' 64 is added to move 0 crossover away from possible travel. Vertical becomes 128 and horizontal becomes 64 ' ' 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. read 1000, WORD lat, due_north, due_south, due_up, minxy, maxxy, minz, maxz, dummy, azmotor, altmotor, azsensor, altsensor, azeye, alteye Latitude = (((ABS lat) ** 46602) + 5) / 10 ' 128 brads/180 degrees *65535 +5 nearest integer lat = 10*latitude IF LAT.15 THEN Latitude = -Latitude ' If south of the equator. ' ' All sensor data is adjusted by +k (altitude) or +kk (azimuth) prior to use, ' ' except raw sensor data used in due_north, due_south, and due_up registration. k = 128 - due_up 'raw sensor+k = corrected altitude sensor data. 128 = due_up. 64 = horizon. 0 = due_down. (Must add 64 to Path data.) 'Brad altitude = Sensor altitude. Sensor altitude = Brad altitude. south_north = (due_south - due_north) 'crossing 0 does not matter ~ 44-250=50 in byte. work = south_north * 5/3 + due_north 'work = sensor at 300 degree azimuth. ' 'Summer sunset azimuth 300 degrees. 300/360 fraction of circle. south_north*2 = full circle, therefore 2*300/360 = 5/3. kk = 250 - work 'raw sensor+kk = corrected azimuth sensor data. kk=250-work makes sensor 250 at 300 degrees azimuth with raw sensor+kk due_north_c = due_north + kk 'Corrected due_north due_south_c = due_south + kk 'Corrected due_south f = (256 * 128) / south_north 'Brad azimuth = (sensor - due_north_c) */ f f=128/south_north f=brads per sensor. z = (256 * south_north) / 128 'sensor azimuth = due_north_c + (brad azimuth */ z) z=south_north/128 z=sensor per brad 'If tropic_summer AND Azimuth_Path > 190 (west to north) THEN sensor azimuth = due_north_c - ((256 - brad azimuth) */ z) if South_north > 127 then no_overlap work = 256 */ f - 256 ' work = brads of full sensor range (256) minus brads of circle ... work = sensor overlap in brad units delta = work */ z ' Delta = overlap in sensor units. if delta < (minxy + 256 - maxxy) then no_overlap ' no overlap between limits top = 256 - delta + minxy ' Top = sensor delta overlap near 255 sensor units bottom = delta + (256 - maxxy) ' Bottom = sensor delta overlap near 0 sensor units goto overlap no_overlap: delta = 0 top = 255 bottom = 0 overlap: if azMotor then azMotor_A = 11 ' PORTD.2 swap PORTD.3 azMotor_B = 10 else azMotor_A = 10 ' Software will reverse wires if motor or sensor installed backwards. 0 to 7 = PORTB.0 to PORTB.7 azMotor_B = 11 ' Configured in Install subroutine. Used in Set_Motors HBridge subroutine. 8 to 15 = PORTD.0 to PORTD.7 endif if altMotor then altMotor_A = 9 ' PORTD.0 swap PORTD.1 altMotor_B = 8 else altMotor_A = 8 altMotor_B = 9 endif if azSensor then azSensor_X = 10 ' AN9 swap AN10 azSensor_Y = 9 else azSensor_X = 9 azSensor_Y = 10 endif if altsensor then altsensor_X = 11 ' AN11 swap AN12 altsensor_Y = 12 else altsensor_X = 12 altsensor_Y = 11 endif if azeye then azeye_Left = 3 ' AN2 swap AN3 azeye_Right = 2 else azeye_Left = 2 azeye_Right = 3 endif if alteye then alteye_Down = 5 ' AN4 swap AN5 alteye_Up = 4 else altEye_Down = 4 alteye_Up = 5 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 return '********************* Get_Sensors: ' ' Tilt accelerometers on edge like a rolling coin, arctangent of x,y pins returns angle in brad circle 0 to 255. ' ' Pin MEMS range 1.5g to -1.5g = 3.3v to 0.0v. 1.5g = 3.3v/5v*1024 (10 bit ADC) = 675 675/3.3v = 205/v. ' 0.0g = 1.65v = 338 -1.5g = 0.0v ' range 1.0g to -1.0g = 2.75v to 0.55v = 563 to 112. 563 - 112 = delta 450 = 1g to -1g. ' rotation from straight up to straight down 1g to -1g is 128 brads +/-64 (180 degrees +/-90). ' 128 brads/450 = 0.2844 brads/sensor. When sensor at 0g = 1.65v = 338 * 0.2844 = 96 brads ' -96 zeros scale at 0g. (range 450) 225 to -225 * 0.2844 = 64 to -64. ' [sensor * 0.228 - 96 = brads.] Room in byte for double ~ [sensor * 0.288 * 2 - 96 * 2] xw = 0 yw = 0 For smooth = 1 to 8 ADCIN azSensor_X, X ADCIN azSensor_Y, Y x = x >> 6 '10 bit result was left justified. x = x ** 37276 - 192 'double ** 18638-96 xw = Xw + x Y = Y >> 6 Y = Y ** 37276 - 192 Yw = yw + y pause 2 next smooth xb = xw / 8 'result in byte varible yb = yw / 8 if xb = 0 and yb = 0 then stow.0 = 1 ' azimuth MEMS failure tag ########### Azimuth_Stow = yes Altitude_Stow = yes ENDIF Azimuth_sensor = Xb ATN Yb ' Arctangent Azimuth_sensor = Azimuth_sensor + kk ' Sensor corrected kk to make sensor 250 (near limit of 255) at 300 degrees. xw = 0 yw = 0 For smooth = 1 to 8 ADCIN altSensor_X, X ADCIN altSensor_Y, Y x = x >> 6 '10 bit result was left justified. x = x ** 37276 - 192 'double ** 18638-96 xw = Xw + x Y = Y >> 6 Y = Y ** 37276 - 192 Yw = yw + y pause 2 next smooth xb = xw / 8 'result in byte varible yb = yw / 8 if xb = 0 and yb = 0 then stow.1 = 1 ' altitude MEMS failure tag ########### Azimuth_Stow = yes Altitude_Stow = yes ENDIF Altitude_sensor = Xb ATN Yb ' Arctangent Altitude_sensor = Altitude_sensor + k ' Sensor corrected k to make sensor 128 at due_up. if Altitude_sensor < (maxZ - Altitude_Gap) then up_limit = no else up_limit = yes endif if Altitude_sensor > (minZ + Altitude_Gap) THEN down_limit = no else down_limit = yes endif if latitude.7 then south_latitude if azimuth_sensor < (maxxy - azimuth_gap) then right_limit = no else right_limit = yes endif if azimuth_sensor > (minxy + azimuth_gap) then left_limit = no else left_limit = yes endif goto done south_latitude: if azimuth_sensor < (maxxy - azimuth_gap) then left_limit = no else left_limit = yes endif if azimuth_sensor > (minxy + azimuth_gap) then right_limit = no else right_limit = yes endif done: 'due_north_c = due_north + kk 'Corrected due_north 'f = (256 * 128) / south_north 'Brad azimuth = (sensor - due_north_c) */ f f=128/south_north f=brads per sensor. 'z = (256 * south_north) / 128 'sensor azimuth = due_north_c + (brad azimuth */ z) z=south_north/128 z=sensor per brad 'If tropic_summer AND Azimuth_Path > 190 (west to north) THEN sensor azimuth = due_north_c - ((256 - brad azimuth) */ z) Altitude_Sensor_Brads = altitude_sensor if azimuth_sensor < due_north_c then Azimuth_Sensor_Brads = 256 - ((due_north_c - azimuth_sensor) */ f) else Azimuth_Sensor_Brads = (azimuth_sensor - due_north_c) */ f endif return '********************* Power_Cycle: work = abs(new_seconds - old_seconds) previous_second = previous_second - 1 'Prevent ticking time failure in Get_Time from rapid clock check. If work < 8 then ' If <= 14 seconds then stow from power cycle, <= 4 seconds if 10 second sleep cycle after sunset. stow.0 = 1 : stow.7 = 1 ' Brief flip of power switch stows dish. Reset button has no effect. altitude_stow = yes ' Power cycle longer than 14 seconds clears stow. Reset clears stow. azimuth_stow = yes ' Cycle power to stow whole field of dishes. endif Return '********************* Get_Time: I2Cread datapin, clockpin, $d1, 0, [sec,minute,hour,day,date,month,year,dummy,dummy,old_seconds.byte0,old_seconds.byte1] ' 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. if display and print then hserout [12] ' Clear screen 'if display and print then hserout [hex2 month," / ",hex2 date," / 20",hex2 year," ",hex hour,":",hex2 minute,":",hex2 sec] if display and print then hserout [32,hex2 date,".",hex2 month,".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 New_seconds = time * 30 + second/2 ' two second units I2Cwrite datapin, clockpin, $d0, 9, [new_seconds.byte0,new_seconds.byte1] ' Track and record in clock RAM the on-time for power cycle detection. if display and print then hserout [" TIME = ",dec time," Sunflower 17.4.13",13,10,10] pause 10 endif If time = old_time and second = previous_second then stow.2 = 1 ' Time failure tag ########### Azimuth_Stow = yes Altitude_Stow = yes ENDIF previous_second = second ' For checking clock ticking return '********************* Check_Time: 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 = latitude.7 and (abs declination) >= (abs latitude) THEN tropic_summer = 1 ' Sun always in north summer tropics after zero shadow day. work8 = time gosub Get_flags_north ' Flags times of arctangent crossover from north to south in AM and from south to north in PM. 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 tropics during summer. IF flag_north = 1 THEN Azimuth_Path = PRIOR ' Azimuth_Path = arctangent, prior = the other arctangent ~ the one in the north. if dark and Altitude_Path > 62 then dark = no ' Dawn. Sunrise 64 solar_noon = 1 ' Time solar noon flag need automatic update when sun focus due south or due north. Clock is solar noon time, not clock time. reboot = 0 ' Reboot done endif 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 mid day. if tropic_summer THEN flag_north = 1 ' Sun always in north in the tropics during summer. IF flag_north = 1 THEN Azimuth_Path = PRIOR ' North arctangent if dark and Altitude_Path > 62 then dark = no ' Sunrise! if dwell then dwell = dwell - 1 ' Countdown tranistion time 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 and azamps = 0 then azimuth_motor = 1 ' Possible limit switch stop motor if altitude_motor and altamps = 0 then Altitude_Motor = 1 iF Azimuth_Motor > 3 THEN ' = 4 could be as little as 2 minutes, start at end of minute 1 and end begining minute 4. Azimuth_Motor = 1 ' reset motor counting for continuing motor checking. travel = Azimuth_sensor - azimuth_move ' Distance moved travel = abs travel IF travel <= Azimuth_Gap THEN ' If no movement within sensor error stow.3 = 1 ' Azimuth motor failed to move tag ########### Altitude_Stow = yes ELSE ' 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 > 3 THEN ' = 4 could be as little as 2 minutes, start at end of minute 1 and end begining minute 4. Altitude_Motor = 1 ' reset motor counting for continuing motor checking. travel = Altitude_sensor - altitude_move ' Distance moved travel = abs travel IF travel <= Altitude_Gap THEN 'If no movement within sensor error stow.4 = 1 ' Altitude motor failed to move tag (check limit switches) ########### Azimuth_Stow = yes ELSE 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 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. need_sunrise = yes ' Flags sunrise calculation not 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 need_sunrise and altitude_path > 63 then ' Sunrise likely 64 but definitely not 63 just below horizon. if time > 360 and time < 1080 then dawn = due_north_c + (Azimuth_Path */ z) ' Azimuth sensor location at sunrise. else ' If 6 am to 6 pm then dawn arctan azimuth_path else dawn arctan prior. dawn = due_north_c + (prior */ z) endif need_sunrise = no ' Sunrise calculated. Do dawn just once when altitude path is 64, or almost 64. 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_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. if latitude.7 then ' If southern latitudes Azimuth_Path = 128 - Azimuth_Path prior = 128 - prior endif Altitude_Path = Altitude_Path + 64 ' Make due_up = 128 and horizon 64 to avoid sensor 0 crossover 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 FOR Smooth = 1 TO work1 adcin alteye_up, work work = work >> 6 north = north + work adcin alteye_down, work work = work >> 6 south = south + work adcin azeye_left, work work = work >> 6 east = east + work adcin azeye_right, work work = work >> 6 west = west + work NEXT smooth north = north / work1 south = south / work1 east = east / work1 west = west / work1 if display and print 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] pause 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 < sunny OR east < sunny OR (west + east) < (sunny2 * 2) OR north < sunny OR south < sunny OR (north + south) < (sunny2 * 2) THEN cloudy = no sunny = sun sunny2 = sun2 ELSE cloudy = yes: sunny = (sun - edge) sunny2 = (sun2 - edge) if Altitude_Path < 64 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 + (sun_gap/2)) and azimuth_command = left ) or ( east <= (west + (sun_gap/2)) 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 + (sun_gap/2)) and altitude_command = down) or (south <= (north + (sun_gap/2)) and altitude_command = up) then altitude_go = no 'Stop motor if eyes equal or past equal if abs (north - south) > sun_gap THEN altitude_go = yes 'Start motor if eyes not near equal. 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 and sw1+sw2+sw3+sw4=4 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 - Altitude_Sensor_Brads ' 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 bright clouds. IF ((altitude_path_sense > altitude_sensor) AND Altitude_command = down) OR ((altitude_path_sense < altitude_sensor) AND Altitude_command = up) THEN cloudy = yes ' stop because tracking the wrong direction RETURN ENDIF ENDIF if Altitude_Path > brads_overhead then skip_solar_danger ' if sun overhead then skip azimuth check. wb = azimuth_path - Azimuth_Sensor_Brads wb = abs wb IF Azimuth_go = yes AND (wb > danger or left_limit or right_limit) THEN ' Distance between dish pointing and solar position. Bright cloud tracking window if latitude.7 then south_lat if (Azimuth_Sensor_Brads > 191 and Azimuth_Path < 64 and azimuth_command = left) or (Azimuth_Sensor_Brads < 64 and Azimuth_Path > 191 and azimuth_command = right) then ' if zero brad between path and sensor while tracking north sun in the tropics cloudy = yes RETURN ENDIF ' if azimuth_sensor < due_north_c then ' Azimuth_Sensor_Brads = 256 - ((due_north_c - azimuth_sensor) */ f) 'else ' Azimuth_Sensor_Brads = (azimuth_sensor - due_north_c) */ f 'endif ' Azimuth_Path_sense = due_north_c + (Azimuth_Path */ z) ' Converts calculated Path Brads to specific dish sensor type data. ' If tropical sun azimuth path crosses north 0 to 255 tracking left towards west. 'if tropic_summer and Azimuth_Path > 127 then Azimuth_Path_sense = due_north_c - ((256-Azimuth_Path) */ z) if ((Azimuth_Path > Azimuth_Sensor_Brads or left_limit) AND azimuth_command = left) or ((Azimuth_Path < Azimuth_Sensor_Brads or right_limit) AND azimuth_command = right) then cloudy = yes RETURN endif goto done_lat south_lat: 'southern latitudes if (Azimuth_Sensor_Brads > 191 and Azimuth_Path < 64 and azimuth_command = right) or (Azimuth_Sensor_Brads < 64 and Azimuth_Path > 191 and azimuth_command = left) then ' if zero brad between path and sensor while tracking south sun in the tropics cloudy = yes RETURN ENDIF if ((Azimuth_Path > Azimuth_Sensor_Brads or right_limit) AND azimuth_command = right) or ((Azimuth_Path < Azimuth_Sensor_Brads or left_limit) AND azimuth_command = left) then cloudy = yes RETURN endif done_lat: ENDIF skip_solar_danger: IF Focus THEN if Altitude_Path > brads_overhead then skip_solar_noon ' Azimuth position not accurate nor needed when sun is directly overhead ' in the tropics a few days per year when declination = latitude. IF solar_noon and (Azimuth_sensor = due_south_c or Azimuth_sensor = due_north_c) THEN ' If focused on sun directly south or north IF ABS(Time - 720) > 60 THEN ' Then if clock is not within 60 minutes stow.0 = 1: stow.1 = 1 ' Solar_noon clock/position error. ########### altitude_stow = yes azimuth_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 ' Writes corrected sensor data (k,kk) 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 wb = abs wb :if wb */ f > 30 then no_memory ' North/South tropics "zero shadow day" cross over in memory. Azwb = (WB * dat1)/(dat1+dat2) ' Calculate scale and offset to dead reckon between ' earlier and later sun memory data. WB = altFuture - altPast wb = abs wb Altwb = (WB * dat1)/(dat1+dat2) IF azPast > azFuture THEN Azimuth_Memory = azPast - Azwb ELSE Azimuth_Memory = azPast + Azwb ENDIF IF altPast > altFuture THEN Altitude_Memory = altPast - Altwb ELSE Altitude_Memory = altPast + Altwb ENDIF GOTO Remember no_memory: Azimuth_Memory = 0 Altitude_Memory = 0 altitude_memory_brads = 0 Azimuth_Memory_Brads = 0 goto no_brads Remember: if azimuth_Memory < due_north_c then Azimuth_Memory_Brads = 256 - ((due_north_c - azimuth_Memory) */ f) else Azimuth_Memory_Brads = (azimuth_Memory - due_north_c) */ f endif altitude_memory_brads = altitude_memory no_brads: if display then Pointer = ((Time / 4 )-30)*3 ' To display age via declination of old data. IF POINTER > 896 THEN RETURN read pointer,wb,wb,old_declination endif return '********************* Set_Motors: ' sw1 ~ sw4 = switches 1 ~ 4 if s1<>sw1 or s2<>sw2 then speedalt = 0 if s3<>sw3 or s4<>sw4 then speedaz = 0 s1=sw1:s2=sw2:s3=sw3:s4=sw4 IF sw1 = 0 and sw2 = 0 THEN altitude_go = no ' If both switches flipped then stop altitude motor. if sw1 <> sw2 then altitude_go = yes speedalt = maxalt endif IF sw1 = 0 AND sw2 = 1 THEN altitude_command = up ' If one switch flipped then altitude up or down. IF sw1 = 1 AND sw2 = 0 THEN altitude_command = down ' If one switch flipped then altitude up or down. IF sw3 = 0 and sw4 = 0 THEN azimuth_go = no ' If both switches flipped then stop azimuth motor. if sw3 <> sw4 then azimuth_go = yes speedaz = maxaz endif IF sw3 = 0 AND sw4 = 1 THEN azimuth_command = right ' If one switch flipped then azimuth right or left. IF sw3 = 1 AND sw4 = 0 THEN azimuth_command = left ' If one switch flipped then azimuth right or left. Motor_Switch_Bypass: ' Use when keyboard controls motors if no_limits then skip_limits gosub get_sensors If (up_limit and altitude_command = up) or (down_limit and altitude_command = down) OR alt_fail then altitude_go = no If (right_limit and azimuth_command = right) or (left_limit and azimuth_command = left) or az_fail then azimuth_go = no Skip_Limits: 'if left_limit and azimuth_command = left then azimuth_go = no ' Limit azimuth left switch. Altitude drive has built in limit switches. 'if right_limit and azimuth_command = right then azimuth_go = no ' Limit azimuth right switch. 'if up_limit and altitude_command = up then altitude_go = no 'if down_limit and altitude_command = down then altitude_go = no IF azimuth_go = no AND altitude_go = NO AND azimuth_on = NO AND altitude_on = NO THEN goto No_Change else warm = yes ' Enable HBridge endif IF azimuth_on = yes AND (azimuth_command <> azimuth_spin or azimuth_go = no or old_speedaz > speedaz) THEN ' Stop before reverse. do ' reduce speed to zero in less than a second wb = old_speedaz old_speedaz = wb - 2 ' Slow azimuth if old_speedaz > wb then old_speedaz = 0 ' If azimuth speed crosses over from 0 to 255 then azimuth speed = 0 hpwm 1, old_SPEEDAZ, freq ' Set PMW to HBridge pause 8 loop until old_speedaz = 0 ' Exit when motor speed is zero speedaz = 0 ' set speed command to zero this loop low azmotor_a ' Brake azimuth motor pause 1 low azmotor_b azimuth_on = no holdaz = no pause 8 endif IF altitude_on = yes AND (altitude_command <> altitude_spin or altitude_go = no or old_speedalt > speedalt) THEN ' Stop before reverse. do ' Reduce speed to zero in less than a second. wb = old_speedalt old_speedalt = wb - 2 ' Slow altitude. if old_speedalt > wb then old_speedalt = 0 ' If altitude speed crosses over from 0 to 255 then altitude speed = 0 hpwm 2,old_speedalt, freq ' Set PMW to HBridge pause 8 loop until old_speedalt = 0 ' Exit when motor speed is zero speedalt = 0 low altmotor_a ' Brake altitude pause 1 low altmotor_b altitude_on = no holdalt = no pause 8 endif Azimuth: if old_speedaz = maxaz or azimuth_go = no or holdaz then altitude ' If maximum Azimuth speed or Azimuth motor off then no need to change speed. if speedaz < startaz then speedaz = startaz ' increment speed command ' 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 = no and azimuth_go = yes if azimuth_on = no and azimuth_go then ' If off and and need to go on then set direction first. if azimuth_command = right then ' Both A and B were off from previous brake low azmotor_b high azmotor_a else low azmotor_a high azmotor_b endif pause 1 endif 'hpwm 1, SPEEDAZ, freq 'set pwm ' Set azimuth speed. do if old_speedaz < startaz then old_speedaz = startaz wb = Old_speedaz old_speedaz = wb + 1 if old_speedaz < wb or old_speedaz > speedaz then old_speedaz = speedaz hpwm 1, old_speedaz, freq pause 30 loop until old_speedaz = speedaz Altitude: if old_speedalt = maxalt or Altitude_go = no or holdalt then good ' If maximum Altitude speed or Altitude motor off then no need to change speed. if speedAlt < startalt 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 = no and altitude_go then ' If off and and need to go on then set direction first. if altitude_command = up then ' Both A and B were off from previous brake low altmotor_b high altmotor_a else low altMotor_a high altmotor_b endif pause 1 endif 'hpwm 2,speedalt, freq 'set pwm do if old_speedalt < startalt then old_speedalt = startalt wb = Old_speedalt old_speedalt = wb + 1 if old_speedalt < wb or old_speedalt > speedalt then old_speedalt = speedalt hpwm 2, old_speedalt, freq pause 30 loop until old_speedalt = speedalt 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. No_Change: 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 IF azimuth_on = no AND altitude_on = NO THEN warm = no ' Sleep HBridge gosub get_amps if display and print 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 [" ",dec azamps," ma ",dec altamps," ma",10,13] pause 10 endif return '********************* Check_Motors: ' Looks at amps used by motors if azimuth_go = no and altitude_go = no then return pause 1 If AzAmps > 5000 then az_fail = yes ' Can surge to 5000 ma steady maximum heat rejection <3000 ma. gosub Motor_Switch_Bypass endif if AltAmps > 5000 then alt_fail = yes gosub Motor_Switch_Bypass endif if skip_amps then exit_check ' Skip motor check when full speed amps < 10 ma (zero amp reading) ' Below purpose is to seperate motor amp failure from physical limit switch. if AzAmps and azimuth_command = left then right_limit_switch = no if AzAmps and azimuth_command = right then left_limit_switch = no if AltAmps and altitude_command = down then up_limit_switch = no if AltAmps and altitude_command = up then down_limit_switch = no if (altamps or altitude_go = no) and (azamps or azimuth_go = no) then return if AzAmps = 0 and azimuth_go = yes and speedaz = maxaz and azimuth_command = right and right_limit_switch = no then azimuth_command = left holdalt = yes gosub Motor_Switch_Bypass speedaz = (maxaz+startaz)/2 gosub Motor_Switch_Bypass if AzAmps > 0 then ' If power in only one direction then possible mechanical limit switch right_limit_switch = yes else az_fail = yes endif azimuth_command = right ' restore prior motor command gosub Motor_Switch_Bypass speedaz = maxaz holdalt = no endif if AzAmps = 0 and azimuth_go = yes and speedaz = maxaz and azimuth_command = left and left_limit_switch = no then azimuth_command = right holdalt = yes gosub Motor_Switch_Bypass speedaz = (maxaz+startaz)/2 gosub Motor_Switch_Bypass if AzAmps > 0 then ' If power in only one direction then possible mechanical limit switch left_limit_switch = yes else az_fail = yes endif azimuth_command = left ' restore prior motor command gosub Motor_Switch_Bypass speedaz = maxaz holdalt = no endif if AltAmps = 0 and Altitude_go = yes and speedAlt = maxAlt and altitude_command = up and up_limit_switch = no then altitude_command = down holdaz = yes gosub Motor_Switch_Bypass speedalt = (maxalt+startalt)/2 gosub Motor_Switch_Bypass if AltAmps > 0 then ' If power in only one direction then possible mechanical limit switch up_limit_switch = yes else alt_fail = yes endif altitude_command = up ' restore prior motor command gosub Motor_Switch_Bypass speedalt = maxalt holdaz = no endif if AltAmps = 0 and Altitude_go = yes and speedAlt = maxAlt and altitude_command = down and down_limit_switch = no then altitude_command = up holdaz = yes gosub Motor_Switch_Bypass speedalt = (maxalt+startalt)/2 gosub Motor_Switch_Bypass if AltAmps > 0 then ' If power in only one direction then possible mechanical limit switch down_limit_switch = yes else alt_fail = yes endif altitude_command = down ' restore prior motor command gosub Motor_Switch_Bypass speedalt = maxalt holdaz = no endif Exit_Check: if az_fail then stow.6 = 1 ' Azimuth motor failed to power tag ########### Altitude_Stow = yes endif if alt_fail then stow.7 = 1 ' Altitude motor failed to power tag (check limit switches) ########### Azimuth_Stow = yes endif return '********************* Get_Amps: xw = 0 yw = 0 For smooth = 1 to 16 adcin AzAmp_Pin, AzAmps ' 525 mV per amp azamps = azamps >> 6 adcin altamp_pin, AltAmps ' 1024/5V 10 bit data 205/V * 0.525V/A = 107/A Data/107*1000=ma 1000/107=9.346 9.346*256=2393 altamps = altamps >> 6 AzAmps = AzAmps */ 2393 ' milliamps AltAmps = AltAmps */ 2393 ' ma if xw < azamps then xw = azamps if yw < altamps then yw = altamps pause 2 next smooth azamps = xw altamps = yw 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. text = 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: pause 10 '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 main 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: no_limits = yes HSEROUT [12] GOSUB GET_SENSORS GOSUB GET_TIME hserout [13,10," Azimuth Altitude",10,13,10] GOSUB Eyes_after_dark hserout [10,"Sensor ",dec Azimuth_sensor, " ",dec Altitude_sensor,10,13] wb = Azimuth_sensor - kk wb1 = Altitude_sensor - k hserout ["Sensor Raw ",dec wb, " ",dec wb1,13,10] GOSUB Motor_Switch_Bypass speedaz = maxaz speedalt = maxalt 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_c) */ f ' Converts azimuth sensor data to brads. WB2 = ALTITUDE_Memory ' 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_c) */ f ' Converts azimuth sensor data to brads. WB2 = ALTITUDE_SENSOR ' 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 Amps",13,10] hserout [ "10000000 Altitude Amps",13,10] hserout [ "00000011 Solar noon Clock",13,10] hserout [ "10000001 Power cycle",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: no_limits = yes read 1000, WORD lat, due_north, due_south, due_up, minxy, maxxy, minz, maxz, dummy, azmotor, altmotor, azsensor, altsensor, azeye, alteye gosub get_sensors HSEROUT [12] HSEROUT [" Latitude degrees times 10 Example: Seattle = 475 ",13,10,10] ' Negative Latitude below equator. wb = Azimuth_sensor - kk wb1 = Altitude_sensor - k hserout ["Sensor Raw Azimuth ",dec wb, " Raw Altitude ",dec wb1,13,10,10] HSEROUT ["0 Latitude ",Sdec lat,13,10] HSEROUT ["1 Refresh ",13,10] if latitude.7 then HSEROUT ["2 Due South ",dec due_north," Raw",13,10] ' If southern latitudes reverse north/south install HSEROUT ["3 Due North ",dec due_south," Raw",13,10] else ' Use Polaris star, solar noon, or Google maps to find true north. HSEROUT ["2 Due North ",dec due_north," Raw",13,10] ' Use Polaris star, solar noon, or Google maps to find true north. HSEROUT ["3 Due South ",dec due_south," Raw",13,10] endif HSEROUT ["4 Due Up ",dec due_up," Raw",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] install_loop: If Hot_Install = no then skip_switches if sw1=sw2 then altitude_go = no if sw3=sw4 then azimuth_go = no print = no gosub set_motors ' ramp motors speed up speedaz = maxaz speedalt = maxalt gosub set_motors print = yes if s1=sw1 and s2=sw2 and s3=sw3 and s4=sw4 then skip_switches s1=sw1:s2=sw2:s3=sw3:s4=sw4 If (sw1=0 and sw2=0) or (sw3=0 and sw4=0) then set_due Skip_Switches: hserin 300,install_loop,[hex1 wb] ' if timeout pause ms, then goto install_loop, or input WB IF WB > $0F THEN INSTALL_loop 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 gosub register goto install Set_Due: azimuth_go = no ' Stop azimuth motor altitude_go = no ' Stop altitude motor GOSUB Motor_Switch_Bypass ' Stop switches from controlling motors gosub get_sensors if sw3 = 0 and sw4 = 0 and sw1 = 0 and sw2 = 1 then ' if both azimuth switches 3,4 on and up switch 1 on then log due_up raw sensor data wb = 4 wb1 = Altitude_sensor - k ' Raw sensor data write 1000 + wb, wb1 ' Write to EEPROM raw due_up value. hserout [7] ' Beep computer indicating EEPROM data write endif if sw3 = 0 and sw4 = 1 and sw1 = 0 and sw2 = 0 then ' if both altitude switches 1,2 on and right switch 3 on then log due_south raw sensor data wb = 3 wb1 = Azimuth_sensor - kk ' Raw sensor data write 1000 + wb, wb1 ' Write to EEPROM raw due_south value. hserout [7] ' Beep computer indicating EEPROM data write endif if sw3 = 1 and sw4 = 0 and sw1 = 0 and sw2 = 0 then ' if both altitude switches 1,2 on and left switch 4 on then log due_north raw sensor data wb = 2 wb1 = Azimuth_sensor - kk ' Raw sensor data write 1000 + wb, wb1 ' Write to EEPROM raw due_north value. hserout [7] ' Beep computer indicating EEPROM data write endif gosub register goto install goto ending