- 1. A learning experience
- 2. For the fun of it
- 3. To develop the software, sensing technique, and mechanical considerations that will be used when constructing a much larger one.
The panel will track the sun only in one axis - after "playing" with this one for a while, I am DEFINITELY going to include a dual axis tracker on the large one.
I used a serial LCD backpak to help troubleshoot the code, simply adding serout2 commands whenever I found something that did not work right (Plenty of that). It proved to be invaluable. Some of these are still in the program, and can be commented out or removed altogether. They are shown in the video. The Serial LCD Backpak is one I constructed & programmed using the HEX code from SPARKFUN. It certainly is not required anymore.
I added some hysteresis (a 25 MV "dead band") when comparing the LED's output. It prevents the panel from "hunting" when it gets close, since exact alignment is not required. I have not seen any erratic readings from the ADC, it is surprisingly stable, and the hysteresis also helps take care of that. Actually, if a very high or low sample occurs, it will self-correct almost immediately, since each time after the panel moves another sample is taken, and the panel will move and adjust accordingly. I thought about averaging several samples, but found that not to be necessary. I have not observed any radical movements yet regarding false analog readings. The occasional cloud has no effect, since both sensors "see" the same amount of sunlight. All that might happen is if clouds persist for a long period, the panel might not adjust till after the sun re-appears. I also added 0.01 mfd caps in parallel with the LED's.
It took a while for me to decide on the best method of positioning the LED's. The Panel back is made of 1/4" Masonite, and the LED's are mounted through a hole on each end of the panel, so just the "dome" is exposed. At first I left that as is, but the readings were all over the ball park. I then used a couple of short opaque "tubes" over each LED, but that did not work as well as I wanted either. Finally I used the technique of placing a "shade" next to each LED, which requires that the panel be nearly perfectly aligned with the sun for each LED to get the same amount of sunlight. That approach is absolutely excellent. The "shades" are just another piece of Masonite, 2" wide by 3" tall. The LED's could have been positioned in the center of the panel, off to one side, using just one shade between them. I liked placing them on the ends better.
A SPDT Center- Off Toggle switch is used to manually power the motor (via software) to move the panel to adjust it when starting. Not necessary, just faster. Two small limit switches are used to prevent panel overtravel. Since I do not plan on leaving it out overnight, I did not see a need to auto-position the panel in the morning. I added the limit switches in case I forget to bring it in to the house after charging the batteries. They certainly could be used along with the necessary code to auto-position the panel however. If a limit switch is closed, the panel will move to a nearly horizontal position and stop, a Power reset is required. (Adding a pushbutton for reset should probably have been included)
The diodes I used with the H-Bridge are probably "overkill", but I have several on hand. Ideally an L293D or equivalent would be better (no external diodes needed).
I really "scored" on some small solar panels on eBay. I bought 4 of them - $5.00 ea. They are 5V @ 200Ma, so I paralleled them to get 5V @ 800Ma. Plenty for charging AA, AAA, C or D NiMh batteries. Presently I am using a "Chi-Com" USB powered battery charger, using the solar panel output rather than the USB port. It was really cheap when the associated parts needed to build one are considered. The batteries will charge as fast on solar as they would using the USB output. One of these days I will build a charger that provides a fast charge option using a MAX 712 or better yet a LTC4060.
I would like to point out that the time for motor-on, the "dead band" value, and the length of the shades I used were totally arbitrary - kind of by guess and by gosh. I started out with a 50 mv "Dead Band", changed it to 30, and finally arrived at 25 mv. Improvements might be possible, but just how much would be gained is questionable.
The program is heavily commented, so it should be easy to follow (as well as pointing some dumb techniques I probably used). Constructive comments would be most welcome. I used DT's timer interrupt example to determine the position sampling period. The period could be changed by changing the counter. The video is not all that good quality, but gets some points across. On the schematic, the symbols for the SPDT center off toggle switch, and for the limit switches, are not standard. I could not find a library symbol for them, so "made them up"
Here is the code:
'************************************************* ***************************** '* Name : SOLAR_TRACKER.BAS * '* A SMALL SOLAR TRACKER / CHARGER FOR CHARGING NiMh BATTERIES * * '* Author : KEN STUEMPGES * '* Date : 7/11/2011 * '* Version : 1.0 * '* Notes : MY VERSION OF A PIC - BASED SOLAR TRACKER * '* IT USES A PIC 16F876A (CAUSE I HAVE LOTS OF THEM). THE SENSORS * '* ARE 10 MM CLEAR LENSE GREEN LED'S. THE ONES I USED WILL * '* OUTPUT AS MUCH AS 1.6 VOLTS IN FULL SUNLIGHT. * '* I USED A SERIAL LCD BACKPAK FOR TROUBLESHOOTING PURPOSES * '************************************************* ***************************** ' ' INCLUDE "DT_INTS-14.bas" INCLUDE "ReEnterPBP.bas" ' DEFINE OSC 4 DEFINE ADC_BITS 10 DEFINE ADC_CLOCK 3 DEFINE ADC_SAMPLEUS 100 ' ADCON1 = 000010 ' RIGHT JUSTIFY TRISC = 001111 TRISA = 111111 TRISB = 110000 ' wsave var byte $70 SYSTEM east var word ' EAST FACING LED, CATHODE TO GROUND, ANODE TO A.0 west var word ' WEST FACING LED, CATHODE TO GROUND, ANODE TO A.1 DIFFERENCE VAR WORD ' used in LCD routine for testing X var WORD ' counter for delay time between readings CHK_POSITION VAR Bit ' TELLS US WHEN TO SAMPLE VIA TIMER INTERRUPT LS_ERROR VAR byte ' TRAVEL LIMIT SWITCH HAS BEEN CLOSED volts1 var word ' used to calculate actual voltage in MV volts2 var word ' ditto conv1 con 4 ' units result of 5000/1024 conv2 con 88 ' hundreths result of 5000/1024 ' EnableMotor var PORTB.2 ' Enables H-Bridge driver RotateWest var PORTB.1 ' H-Bridge input to Rotate motor toward west RotateEast VAR PORTC.6 ' H-Bridge input to Rotate Motor toward east GoWest var PORTC.0 ' Toggle switch to rotate panel west GoEast var PORTC.1 ' Toggle switch to rotate panel east StopWest var PORTC.2 ' Limit Switch to stop West rotation StopEast var PORTC.3 ' Limit Switch to stop East rotation Tx var PORTC.4 ' TRANSMIT TO SERIAL LCD BACKPAK ' EnableMotor = 0 RotateWest = 0 RotateEast = 0 CHK_POSITION = 0 x = 0 LS_ERROR = 0 ' ASM INT_LIST macro ; IntSource, Label, Type, ResetFlag? INT_Handler TMR1_INT, _TIME_COUNTER, PBP, yes endm INT_CREATE ; Creates the interrupt processor ENDASM ' T1CON = $31 ; Prescaler = 8, TMR1ON @ INT_ENABLE TMR1_INT ; enable Timer 1 interrupts '********************** goto main '************************************************* ************** '************************************************* ************* '' MoveEast: ' move off travel limit switch @ INT_DISABLE TMR1_INT ' DISABLE TIMER1 INTERRUPT WHILE MOVING while stopwest = 0 'LIMIT SWITCH CLOSED ? EnableMotor = 1 RotateEast = 1 ' MOVE PANEL OFF LIMIT SWITCH wend pause 8000 ' move panel until it is nearly parallel with earth eNABLEmOTOR = 0 rOTATEEast = 0 @ INT_ENABLE TMR1_INT RETURN MoveWest: ' MOVE OFF TRAVEL LIMIT SWITCH @ INT_DISABLE TMR1_INT ' DISABLE TIMER1 INTERRUPT WHILE MOVING while stopeast = 0 ' LIMIT SWITCH CLOSED ? EnableMotor = 1 RotateWest = 1 wend pause 8000 'move panel until it is nearly parallel with earth eNABLEmOTOR = 0 rOTATEWest = 0 @ INT_ENABLE TMR1_INT RETURN ' AutoEast: if stopeast = 0 then ' TRAVEL LIMIT SWITCH CLOSED ls_error = 1 ' FLAG THE ERROR GOTO EXIT_EAST ' LIMIT SWITCH CLOSED - GET OUT ENDIF EnableMotor = 1 ' ELSE MOVE THE PANEL NORMALLY FOR ADJUSTMENT RotateEast = 1 pause 100 'give it time to move the panel some (THIS SEEMS TO BE THE ' OPTIMUM TIME FOR MOVING THE PANEL APROX 1 DEGREE) EXIT_EAST: eNABLEmOTOR = 0 rOTATEEast = 0 RETURN ' AutoWest: if stopwest = 0 then ' TRAVEL LIMIT SWITCH CLOSED ls_error = 1 ' FLAG THE ERROR GOTO EXIT_WEST 'LIMIT SWITCH CLOSED - GET OUT ENDIF EnableMotor = 1 ' ELSE MOVE PANEL NORMALLY FOR ADJUSTMENT RotateWest = 1 pause 100 ' give it time to move some EXIT_WEST: eNABLEmOTOR = 0 rOTATEWest = 0 RETURN ' ' MOVE THE PANEL USING THE TOGGLE SWITCH ManualWest: @ INT_DISABLE TMR1_INT ' DISABLE TIMER INTERRUPT WHILE MOVING MANUALY While GoWest = 0 EnableMotor = 1 RotateWest = 1 wend EnableMotor = 0 RotateWest = 0 @ INT_ENABLE TMR1_INT return ManualEast: ' MOVE THE PANEL USING THE TOGGLE SWITCH @ INT_DISABLE TMR1_INT ' DISABLE TIMER INTERRUPT WHILE MOVING MANUALY while GoEast = 0 EnableMotor = 1 RotateEast = 1 wend EnableMotor = 0 RotateEast = 0 ' @ INT_ENABLE TMR1_INT return ' Measure: ' NOW CHECK THE POSITION LED SENSORS ' @ INT_DISABLE TMR1_INT ' DISABLE THE TIMER INTERUPT DURING POSITION CHECK / AUTO MOVE ' READ THE ANALOG PORTS ' Automatically move the panel East or West to track the Sun ' IF LS_ERROR = 1 THEN GOTO HERE ' GET OUT IF TRAVEL LIMIT SWITCH IS CLOSED Measure1: ' ENTRY POINT FOR ADJUSTING PANEL ' IF LS_ERROR = 1 THEN GOTO HERE ' GET OUT IF TRAVEL LIMIT SWITCH IS CLOSED ' ADCIN 0,east 'LED'S OUTPUT UP TO 1.5 VOLTS WHEN FULLY EXPOSED TO LIGHT pause 50 ' Convert west LED Millivolt value volts1 = east * conv1 volts2 = east * conv2 volts2 = volts2/100 east = volts1 + volts2 'actual millivolt value of west LED ' Convert the east LED Millivolt value adcin 1,west pause 50 ' volts1 = west * conv1 volts2 = west * conv2 volts2 = volts2/100 west = volts1 + volts2 'actual millivolt value of east LED '************************************************* ****************** ' THIS IS JUST FOR TROUBLESHOOTING USING THE LCD ' the pause 1000 in the serial routine should be replaced in the ' read_east / read_west to provide some delay between movements IF east > west then difference = east - west if west > east then difference = west - east SEROUT2 Tx, 84,[254,192,"EAST: ", dec east] 'DISPLAY THE DECIMAL MV VALUE pause 50 serout2 Tx,84,[254,148,"WEST: ",DEC west] PAUSE 50 serout2 Tx,84,[254,212,"DIFFERENCE ",DEC DIFFERENCE] pause 1000 SEROUT2 Tx,84,[254,$01] ' clears the display '************************************************* ********************** ' IF DIFFERENCE <= 25 THEN GOTO HERE ' NO ADJUSTMENT NEEDED ' Adjust the Panel as needed The +25 value adds hysteresis ("Dead-Band") ' READ_EAST: IF (east+25) > west THEN GOSUB AutoWest ' Need to move westward PAUSE 100 ' ALLOW FOR VIBRATION OF PANEL GOTO MEASURE1 'TAKE ANOTHER READING ENDIF PAUSE 50 ' REPEAT TILL DIFFERENCE IS =< 25 IF (EAST + 25) > WEST THEN GOTO READ_EAST READ_WEST: IF (west+25) > east THEN gosub AutoEast ' Need to move eastward PAUSE 100 ' ALLOW FOR VIBRATION OF PANEL GOTO mEASURE1 ENDIF PAUSE 50 ' REPEAT TILL DIFFERENCE IS =< 25 IF (WEST + 25) > EAST THEN GOTO READ_WEST ' Panel is relatively aligned with the Sun at this point ' Now wait till next reading via the timer interrupt HERE: EAST = 0:WEST=0:DIFFERENCE = 0 CHK_POSITION = 0 ' @ INT_ENABLE TMR1_INT RETURN '************************************************* *********** '************************************************* *************** Main: ' CHECK FOR A MANUAL MOVE If GoWest = 0 then gosub ManualWest ' If GoEast = 0 then gosub ManualEast ' ' CHECK FOR LIMIT SWITCH CLOSED if StopWest = 0 then gosub MoveEast ' move panel off the limit switch loop1: ' *** COULD ADD CHECKING A PUSHBUTTON HERE TO CLEAR ERROR*** goto loop1 ' loop here till power cycle- - panel is in horizontal position endif 'and stopped which indicates a limit switch error ' if StopEast = 0 then gosub MoveWest 'move panel off limit switch loop2: ' *** COULD ADD CHECKING A PUSHBUTTON HERE TO CLEAR ERROR*** goto loop2 'loop here till power cycle- - panel is in horizontal position endif 'and stopped which indicates a limit switch error ' ' IS IT TIME TO READ THE LED SENSORS ? IF CHK_POSITION = 1 THEN GOSUB Measure ' goto Main '---[TMR1 - interrupt handler]-------------------------------------------------- TIME_COUNTER: X = X + 1 ' THE LCD DISPLAY IS FOR TROUBLESHOOTING IF NEEDED ' INDICATES THE TIMER INTERRUPT IS EXECUTING BY DISPLAYING THE COUNT ' SEROUT2 Tx, 84,[254,128,"X ", dec X] 'DISPLAY THE TIMER COUNT IF X = 120 THEN CHK_POSITION = 1 ' sample aprox once a minute IF X >= 122 THEN ' Give it 2 more ticks and start over X = 0 CHK_POSITION = 0 SEROUT2 Tx,84,[254,$01] ' clears the display ENDIF @ INT_RETURN GOTO MAIN ' end
I also have a UTube video you can see HERE
Schematic is located here: tracker.pdf
This little tracker works great . .
Ken
Re: PBP3 Optimization & Peripheral Control Techniques on PIC16F/18F Devices
Yes, all the time and most times in conjuction with DT-Ints for PBP context save/restore. For periodic/cyclic interrupts using TMR2 (or one of the same type) is nice because you set it up once and...
HenrikOlsson Yesterday, 15:27