' {$STAMP BS2} ' {$PBASIC 2.5} ' ' Title: Experiments with Solar Panels ' File: Parallax_Sun_Tracker_Exp.bs2 ' Copyright: LearnOnLine, Inc. ' Author: J. Gavlik ' Last Updated: 09-12-2009 ''------------------------------------------------------------------ ' General VARs ''------------------------------------------------------------------ tempByte VAR Byte tempWord VAR Word ckSum VAR Byte ''------------------------------------------------------------------ ' VARs for MCP3208 8 channel, 12 bit multiplexed A/D Converter ''------------------------------------------------------------------ a2dClk CON 6 'clock (P0) a2dDout CON 7 'data out (P1) a2dDin CON 8 'data in (P2) a2dCs CON 9 'chip select (P3) a2dId0 CON %1000 'A/D Channel 0 ID a2dId1 CON %1001 'A/D Channel 1 ID a2dId2 CON %1010 'A/D Channel 2 ID a2DId3 CON %1011 'A/D Channel 3 ID a2dId4 CON %1100 'A/D Channel 4 ID a2dId5 CON %1101 'A/D Channel 5 ID a2dId6 CON %1110 'A/D Channel 6 ID a2DId7 CON %1111 'A/D Channel 7 ID a2dIdShiftOut VAR Nib 'A/D Channel ID to shift out a2dResult VAR Word '16-bit result of A/D conversion ''------------------------------------------------------------------ ' On Demand Battery Charger VARs ''------------------------------------------------------------------ fullChargeVolts CON 1200 '1.20 volts (1200 millivolts) fullDischargeVolts CON 750 '0.90 volts (900 millivolts) Q1 CON 13 'HIGH when charging, LOW when not chargeLed CON 12 'HIGH when charging, LOW when not minEnergy CON 7350 '7350 milliamp minutes accumulated current measuredEnergy VAR Word 'accumulated current values every minute charging VAR Bit '0 = not charging, 1 = charging energyLoopCtr VAR Byte 'energy loop counter energyLoopCount CON 60 'number of passes through Main to equal 1 minute batteryVolts VAR Word 'battery voltage in millivolts BattOneOhmDrop VAR Word 'voltage drop across 1 ohm resistor connected to battery batteryCurrent VAR Word 'battery current in milliamps ''------------------------------------------------------------------ ' Sun Tracker VARs ''------------------------------------------------------------------ hBridgeEastHiBase CON 2 'motor H-Bridge controls hBridgeEastLoBase CON 0 hBridgeWestHiBase CON 3 hBridgeWestLoBase CON 1 solarVolts VAR Word 'solar panel voltage in millivolts solarOneOhmDrop VAR Word 'voltage drop across 1 ohm resistor connected between 'solar panel and load solarCurrent VAR Word 'solar panel current in milliamps motorOn CON 60 'milliseconds duration for motor ON state lastVolts VAR Word 'last or previous solar panel sample searchTrack VAR Bit '0 = seach for the sun, 1 = track the sun dataLogCtr VAR Byte 'counts passes thru Main before data log event dataLogCount CON 120 'number of passes through Main to equal 2 minutes dataLogPtr VAR Byte 'pointer to logged data for display '------------------------------------------------------------------ ' LED Flash Sequence '------------------------------------------------------------------ ' Data Logging ' once a second - data logging in progress - short flash ' once every 2 minutes - log event - longer 1 second flash ' four rapid flashes - data log memory full ' no flashing - data logging disabled ' ' Battery Charging ' solid on - battery charging ' slow flashing - battery voltage is below minimum while charging ' '------------------------------------------------------------------ ' Initialize Routine '------------------------------------------------------------------ Initialize: searchTrack = 0 'initialize tracking method to search charging = 0 'initialize to not charging battery GOSUB Motor_Stop 'initialize the motor to stop state GOSUB Get_Average_Voltages 'sample the solar panel voltage and lastVolts = solarVolts 'save it for the sun tracker routine dataLogPtr = 0 'pointer for plotting logged data LOW chargeLed 'extinguish the LED '------------------------------------------------------------------ ' Main Routine '------------------------------------------------------------------ Main: PAUSE 250 'adjust this delay so the time through the Main 'loop is approximately 1 second Sun_Tracker: IF (searchTrack = 0) THEN 'decide on how to track the sun GOSUB Sun_Search 'just starting out at the beginning of the day or ELSE GOSUB Sun_Track 'after finding out where the sun is and tracking it ENDIF Battery_Charger: IF (charging = 0) THEN 'decide whether or not to charge battery if GOSUB Test_Battery_Voltage 'battery voltage is above minimum or ELSE GOSUB Charge_Battery 'if it needs charging ENDIF 'LED will remain ON for entire charging cycle Data_Logging: 'Log Data or Not (P11) IF (IN11 = 0) THEN 'if P11 = 0 (ground) disable data logging GOSUB Clear_Logged_Data 'and erase logged memory ELSE 'LED will NOT flash as long as P11 = 0 GOSUB Log_Data 'else, log data every 2 minutes ENDIF 'LED will flash once every time thru Main 'and flash longer when event is logged 'LED will flash 4 times when data log memory 'is full Display_Data: IF (IN15 = 0) THEN 'Display Real Time or Logged Data (P15) GOSUB Plot_Real_Time_Data 'display real time solar data when P15 = 0 ELSE GOSUB Plot_Logged_Data 'display logged solar data when P15 = 1 ENDIF GOTO Main '------------------------------------------------------------------ ' Sun Tracker Routine '------------------------------------------------------------------ Sun_Search: 'do this to establish the original sun position searchTrack = 0 'by moving east until we find it GOSUB Move_East 'move east slightly GOSUB Get_Average_Voltages 'and take another solar panel voltage sample IF (lastVolts > solarVolts) THEN 'we've now moved the panel too far east, so searchTrack = 1 'it's time to track the sun as it moves GOTO Sun_Search_End ENDIF lastVolts = solarVolts 'else, save this voltage for next time Sun_Search_End: RETURN Sun_Track: 'do this when the original position of the sun is found searchTrack = 1 'and we're tracking it GOSUB Get_Average_Voltages 'take another solar panel voltage sample tempWord = solarVolts >> 2 'purposly degrade the solar panel voltage so that 'we don't react to "noise" IF (tempWord = lastVolts) THEN 'test for no sun movement in the last second PAUSE 800 'if so, pause to maintain loop timing and GOTO Sun_Track_End 'exit to Main if no movement ELSE lastVolts = tempWord 'else, save this degraded sample of solarVolts as lastVolts ENDIF GOSUB Move_West 'move solar panel west slightly Sun_Track_East: GOSUB Get_Average_Voltages 'sample solar panel voltage again tempWord = solarVolts >> 2 'purposly degrade the solar panel voltage so that 'we don't react to "noise" IF (tempWord < lastVolts) THEN 'if sample is less than last sample, we've gone too far GOSUB Move_East 'west so move back to where were last time ENDIF Sun_Track_End: RETURN '------------------------------------------------------------------ ' Motor Subroutines '------------------------------------------------------------------ Move_West: HIGH hBridgeEastHiBase HIGH hBridgeWestLoBase PAUSE motorOn GOSUB Motor_Stop RETURN Move_East: HIGH hBridgeWestHiBase HIGH hBridgeEastLoBase PAUSE motorOn GOSUB Motor_Stop RETURN Motor_Stop: LOW hBridgeEastHiBase LOW hBridgeEastLoBase LOW hBridgeWestHiBase LOW hBridgeWestLobase RETURN '------------------------------------------------------------------ ' On Demand Battery Charge Algorithm '------------------------------------------------------------------ ' ' Test_Battery_Voltage: 'stay in this loop as long as the battery is above min voltage ' Disable charging (Q1 = LOW) and extinguish LED ' Get battery voltage reading ' if batteryVolts >= fullChargeVolts, loop back ' else, goto Charge_Battery ' ' Charge_Battery: 'stay in this loop until a minimum charge is delivered into the battery ' Enable charging (Q1 = HIGH) and illuminate LED ' acquire a minimum battery charge ' when minimum battery charge is acquired, branch to Verify_Battery_Charged ' ' Verify_Battery_Charged: 'stay in this loop until the battery is at full voltage ' if batteryVolts >= fullChargeVolts, goto Test_Battery_Voltage ' else, loop back to Verify_Battery_Charged - charging is still taking place ' ' NOTE 1: Battery voltage, current, power or resistance values are NOT displayed ' ' NOTE 2: Only solar panel voltage, current, power and resistance are displayed ' ' NOTE 3: The battery voltage and current have already been sampled in the sun tracker routine above ' Test_Battery_Voltage: charging = 0 'disable charging LOW Q1 'turn Q1 OFF LOW ChargeLed 'extinguish charging LED IF (batteryVolts < fullDischargeVolts) THEN 'test battery voltage for below minimum measuredEnergy = 0 'if so, clear accumulated energy value energyLoopCtr = energyLoopCount 'initialize energy loop count for 1 minute value charging = 1 'set charging flag ENDIF RETURN Charge_Battery: charging = 1 'enable charging HIGH Q1 'turn Q1 ON HIGH ChargeLed 'illuminate charging LED IF (measuredEnergy >= minEnergy) THEN 'test for accumulated ampMinutes of energy GOTO Verify_Battery_Charged 'branch if minimum energy level is met ENDIF energyLoopCtr = energyLoopCtr - 1 'else, decrement energy loop counter IF (energyLoopCtr = 0) THEN 'test for 1 minute equivalent time in loop energyLoopCtr = energyLoopCount 'if so, reinitialize energyLoopCtr and measuredEnergy = measuredEnergy + batteryCurrent 'add latest current reading to measuredEnergy var ENDIF RETURN Verify_Battery_Charged: 'determine if battery voltage is at or above minimums TOGGLE ChargeLed 'flash charging LED to show that we are in this state IF (batteryVolts >= fullChargeVolts) THEN 'test against fully charged voltage charging = 0 'if so, disable charging ENDIF RETURN '------------------------------------------------------------------ ' Get Average Voltages '------------------------------------------------------------------ Get_Average_Voltages: 'zero... batteryVolts = 0 'battery voltage battOneOhmDrop = 0 'battery oneOhmDrop to compute battery current batteryCurrent = 0 'battery current solarVolts = 0 'solar panel voltage solarOneOhmDrop = 0 'solar oneOhmDrop to compute current into load solarCurrent = 0 'solar panel current FOR tempByte = 0 TO 15 'accumulate 16 readings of solar and battery voltages a2dIdShiftOut = a2dId2 'set the a/d converter to the battery GOSUB A2D 'get A/D count batteryVolts = batteryVolts + a2dresult 'accumulate result a2dIdShiftOut = a2dId5 'set the a/d converter to its sense resistor GOSUB A2D 'get A/D count battOneOhmDrop = battOneOhmDrop + a2dresult 'accumulate result a2dIdShiftOut = a2dId0 'set the a/d converter to the solar panel GOSUB A2D 'get A/D count solarVolts = solarVolts + a2dresult 'accumulate result a2dIdShiftOut = a2dId7 'set the a/d converter to its sense resistor GOSUB A2D 'get A/D count solarOneOhmDrop = solarOneOhmDrop + a2dresult 'accumulate result NEXT batteryVolts = batteryVolts / 16 'take averages of 16 samples battOneOhmDrop = battOneOhmDrop / 16 solarVolts = solarVolts / 16 solarOneOhmDrop = solarOneOhmDrop / 16 IF (battOneOhmDrop > batteryVolts) THEN 'test for voltage drop across 1 ohm sense resistor GOTO Get_Average_Voltages 'repeat sample if greater ENDIF IF (solarOneOhmDrop > solarVolts) THEN GOTO Get_Average_Voltages ENDIF 'compute currents batteryVolts = batteryVolts */$0138 'convert to millivolts (1.22mv / count) battOneOhmDrop = battOneOhmDrop */$0138'convert to millivolts (1.22mv / count) batteryCurrent = (batteryVolts - battOneOhmDrop) / 1 'compute the voltage drop across the 1 ohm sense resistor 'which is automatically now in milliamps by the following: ' ' I = E / R where ' I = current in milliamps ' E = voltage in millivolts ' R = resistance in ohms solarVolts = solarVolts */$0138 'convert to millivolts (1.22mv / count) solarOneOhmDrop = solarOneOhmDrop */$0138 'convert to millivolts (1.22mv / count) solarCurrent = (solarVolts - solarOneOhmDrop) / 1 'compute the voltage drop across the 1 ohm sense resistor 'which is automatically now in milliamps by the following: ' ' I = E / R where ' I = current in milliamps ' E = voltage in millivolts ' R = resistance in ohms RETURN '------------------------------------------------------------------ ' Data Logging Routines ' All data logging is done in the programming EEPROM memory of the BS2 ' ' There are 1024 bytes for data log storage ' Each data log record = 4 bytes stored as follows from low to high address ' solarVolts.HIGHBYTE ' solarVolts.LOWBYTE ' solarCurrent.HIGHBYTE ' solarCurrent.LOWBYTE ' This allows 256 records which are recorded approximately once every 2 minutes ' 256 x 2 minutes = 512 minutes ' 512 minutes / 60 min/hr = approximately 8.5 hours of continuous storage ' A pointer to the next data record to be written is stored in $400 and $401 '------------------------------------------------------------------ Clear_Logged_Data: DataLogCtr = 0 'clear the 2 minute counter-timer READ $400, tempWord.HIGHBYTE 'read data log pointer into tempWord READ $401, tempWord.LOWBYTE IF (tempWord = 0) THEN 'test for data log memory already cleared GOTO Clear_Log_Data_End 'i.e, data log pointer is at zero - return if so ENDIF FOR tempWord = 0 TO $3FF 'zero the log memory WRITE tempWord,0 NEXT WRITE $400, 0 'reset data log pointer at $400 and $401 WRITE $401, 0 Clear_Log_Data_End: RETURN Log_Data: READ $400, tempWord.HIGHBYTE 'get current pointer to logged data READ $401, tempWord.LOWBYTE IF (tempWord >= $400) THEN 'test to see if we are at max data log memory FOR tempByte = 0 TO 3 'if so, flash LED 4 times to aleart user of this TOGGLE chargeLED 'set P14 = 0 to clear logged memory PAUSE 100 'then remove jumper to allow more data logging TOGGLE chargeLED PAUSE 100 NEXT GOTO Log_Data_End ENDIF dataLogCtr = dataLogCtr + 1 'else, increment 2 minute counter TOGGLE chargeLED 'quickly flash LED to show data logging in progress PAUSE 10 TOGGLE chargeLED IF (dataLogCtr >= dataLogCount) THEN'test to see if 2 minutes have elaspsed DataLogCtr = 0 'if so, reset 2 minute counter 'write solar panel voltage and current to EEPROM WRITE tempWord + 0, solarVolts.HIGHBYTE WRITE tempWord + 1, solarVolts.LOWBYTE WRITE tempWord + 2, solarCurrent.HIGHBYTE WRITE tempWord + 3, solarCurrent.LOWBYTE tempWord = tempWord + 4 'increment pointer WRITE $400, tempWord.HIGHBYTE 'store it back to EEPROM WRITE $401, tempWord.LOWBYTE TOGGLE chargeLed 'flash chargeLED to indicate data log event PAUSE 1000 'long 1 second flash TOGGLE chargeLed ENDIF Log_Data_End: RETURN '------------------------------------------------------------------ ' Plot Solar Panel Data '------------------------------------------------------------------ Plot_Logged_Data: tempWord = 0 'clear for below tempword.LOWBYTE = dataLogPtr 'compute pointer to logged data from 8-bit counter tempWord = tempWord << 2 'multiply by four to compute address pointer 'value of 4-bytes per data record dataLogPtr = dataLogPtr +1 'increment it for next time thru READ tempWord + 0, solarVolts.HIGHBYTE 'read from EEPROM into solar voltage and current vars READ tempWord + 1, solarVolts.LOWBYTE READ tempWord + 2, solarCurrent.HIGHBYTE READ tempWord + 3, solarCurrent.LOWBYTE 'fall thru to plot realtime data with logged data instead Plot_Real_Time_Data: ckSum = (solarVolts.HIGHBYTE + solarVolts.LOWBYTE + solarCurrent.HIGHBYTE + solarCurrent.LOWBYTE) 'compute checksum DEBUG solarVolts.HIGHBYTE, solarVolts.LOWBYTE, solarCurrent.HIGHBYTE, solarCurrent.LOWBYTE ,ckSum 'Transmit data to computer RETURN '------------------------------------------------------------------ ' A/D Converter Routine ' Enter with a2dIdShiftOut as the channel to convert ' Return with 12-bit result in a2dResult - right shifted '------------------------------------------------------------------ A2D: 'Initialize signals HIGH a2dCs 'Disable A/D chip select HIGH a2dDin 'Initial state of data in LOW a2dClk 'Initial state of clock a2dResult = 0 'Clear the 10-bit result A2D_Start_Conversion: 'Start the conversion process LOW a2dCs 'Enable A/D chip select A2D_Shift_Out_Channel_ID: 'Shift out the Channel ID value PULSOUT a2dClk,10 'Send first clock with Din high SHIFTOUT a2dDin,a2dClk,MSBFIRST,[a2dIdShiftOut\4] A2D_Shift_In_Result: 'Shift in the result PULSOUT a2dClk,10 'clock out null bit PULSOUT a2dClk,10 'clock out null bit SHIFTIN a2dDout,a2dClk,MSBPRE,[a2dResult\12] A2D_End_Conversion: HIGH a2dCs 'Disable A/D chip select RETURN '------------------------------------------------------------------ ' Program End '------------------------------------------------------------------ END