Projects Research & Design Hub

Sun Tracking Project

Written by Jeff Bachiochi

Using PIC18 MCU

Most solar panel arrays are either fixed-position or have a limited field of movement. In this project article, Jeff set out to tackle the challenge of a sun tracking system that can move your solar array to follow the sun’s position. Jeff’s project is a closed-loop system using servos, opto encoders and the Microchip PIC18 MCU.

  • How to build a sun tracking system based on a PIC18 MCU

  • How a solar array works

  • What the program’s main loop does

  • How to do the sensor sampling

  • How to use the MCU’s on-chip oscillator

  • How to interface the LCD

  • Microchip PIC18F2413 MCU

  • EAALST05RDMA0 Ambient Light Sensor from Everlight America

  • LCD117 Serial LCD Board
    from Modern Device

  • Solar panel

I ’ve positioned my hammock in the yard to take advantage of the afternoon sun. I would prefer to have the sunshine over my shoulder when I relax with a good book and “work” on my tan. I can survive for about two hours before the exposure begins to fry unprotected skin like morning bacon sizzling on the stove. That’s usually enough time to bridge a few of chapters of whatever book I happen to be reading at the time. Presently that’s Hidden Figures.

Although the sun can be significant during winter with a blanket of snow reflecting its rays, if there is any wind, wind chill trumps its heat. During the winter months I try to get exposure to real sunlight every day. If you begin to feel depressed during winter months, try spending some time outside. I believe it has physiological qualities that can perk up your doldrums.

We learned in science class that the tilt of the earth is responsible for the seasons. In summer, when the North Pole is tilted toward the sun, its path over us is higher in the sky, so it stays visible longer and creates warmer average temperatures. At the same time, the South Pole is tilted away from the sun, and continents below the equator experience the sun lower in the sky—shorter days and colder average temperatures.

The sun therefore not only takes a path from east to west across the sky every day, but also its arc moves north and south on a yearly basis. If you’re lucky enough to have a south-facing roof, solar panels cab be set due south and tilted to get optimum exposure. There is plenty debate on just what fixed tilt angle is best. This will depend on which season you wish to get the best efficiency. Most of us have less than optimum roof positions, which makes installing solar panels a bad investment. Solar energy companies will do a site survey for free, to see if your roof is a good candidate for panels.

If you have acreage that would allow a free-standing array, you can always get optimum position, provided you have a clear shot to the sun for most its arc across the sky. I’ve seen fixed arrays, arrays that arc across the sky but are fixed at one latitude, and those fully articulated for arc and latitude. Your panels will be most effective if they are fully articulated. While the increase in complexity (and cost) for articulation will better capture the maximum sunlight, compare cost with payback for each type before making any decisions.

Solar arrays are usually set up facing south, so the mechanical movements need move only 180 degrees. I wanted to create an articulated array that can be placed anywhere without regard to any particular direction. This means the movement needs to cover 180 degrees N:S and 180 degrees E:W. This also means no yaw, which would require 360 degrees of movement. Standard servo motors are available with some hardware that makes them easy to use. Figure 1 shows the way these are used to mount a 10 W array to a 1 ½ inch plastic pipe.

FIGURE 1 – Each DC servos used here came with a pair of mechanical brackets. These brackets contain mounting holes on a number of different sides, which allow them to be mounted in various orientations.

Most array mechanics use DC motors with linear actuators for push-pull movement. The only control over the motors is ON:OFF control in both directions. Limit switches protect the motors from being asked to move beyond a safe point at either extreme. And there is no position feedback, except for the end of travel limit switches. Movement for each axis uses two light sensors and one motor. An imbalance between the two light sensors directs the motor to move toward the stronger signal until both sensors are outputting equal signals. At this point the sensors should be directly facing the sun, or the strongest light source.

This constitutes a closed-loop system. If more than two axes are used, each one moves independently. The advantage of using servos is that they give position feedback for each axis. If logging data, then you can get a picture of how the array moves, which can be affected by clouds or other interfering entities. You’ll note my arrangement of servos allows 180 degree coverage in both axes. All will be referenced to 0 degrees, the servos centered, and the array pointing directly up. Not only is this where the sun will be at noon at the equator, but more importantly, it also gives the sensors the ability to see in any direction toward the horizon where morning light will first shine.

Let’s take a look at how the sensors determine the sun’s position. Opto encoders are used to determine wheel movement by an interference wheel that alternately allows light to pass to two opto devices (Figure 2). The interference spokes and the opto devices are positioned such that only one device at a time sees light as a spoke passes in front of them. Count the light pulses. and you get fractions of a revolution. Basically, we are using the same shadow effect with analog light sensors by placing a vertical wall between them. Let’s assume we are at the equator, it’s noon and the array is pointing up—0 degrees on each axis. The height of this wall between the sensors and the placement of the sensors determines how far the sun must travel before it casts a shadow upon one sensor (Figure 3).

FIGURE 2 – Opto encoders determine direction and motion by spokes attached to the wheel, alternately shadowing one of two light sensors.
FIGURE 3 – Similar to the opto encoder, a barrier next to a light sensor produces a shadow on the sensor whenever the sun moves off-axis to the barrier. This produces a lower output on the shadowed sensor.

We can determine either time/degree or degree/time with a little math. The earth takes 24 hours to rotate 360 degrees—that’s one day. Each hour it travels 360/24 or 15 degrees. With the sensor pointed directly at the sun, no shadow is cast on any of the four light sensors. In a perfect world, each sensor’s output would be the same. As more light hits a sensor, its output goes up. This means that there will be little output in low light, and large output in strong sun. They will not be the same, but they should be close. Expecting them to be identical might be beyond the positioning resolution, so it’s best to leave some (non-) wiggle room.

Please refer to Figure 4 for the flow chart used for this project. The main loop spends most of its time waiting for time to pass. Periodically, the light sensors are sampled, and the LCD is updated with the time, light values and position angles (Figure 5). The time comes from an RTC. The CalculatePositionError routine is responsible for determining the relationship between the North and South sensors and the East and West sensors. This relationship is displayed along with the light values for each sensor. The position angle in degrees is shown for each servo. Initially both servos were set to move to 0 degrees, centered, and the solar panel aimed straight up.

FIGURE 4 – Ordinarily, a solar array is moved by a comparator’s output in a closed-loop system requiring no computing power. I created this platform to reap information from the system, which uses DC servos for positioning and feedback from quadrant light sensors. Status of the light sensors determines if any servo movement is required on either of two independent axes.
FIGURE 5 – Periodic sampling of the light sensors is displayed in real time on an LCD, along with the servo positions that are updated once a minute.

Once every minute the results from the CalculatePositionError routine are used to determine if either servo needs to move. If the Flags.SEQN (South=North) is set, the N:S sensor pairs are equal, their difference is within the accepted tolerance and no servo adjustment is necessary. If the Flags.SGTN (South>North) is set, then NSDegree is decremented and the MoveServo routine is called. If the Flags.SLTN (South<North) is set, then NSDegree is incremented and the MoveServo routine is called. The same logic is performed on the E:W sensor pair. The sensors are sampled again, and the CalculatePositionError routine determines if another movement is necessary. Once the “EQ” flags for both axes are set, the solar array and light sensor module have again been realigned with the sun and the routine is finished.

Those familiar with controlling an RC servo know that a control pulse width of from 1 ms to 2 ms will position a servo between 0 and 90 degrees. For those servos that have a 180-degree range, the control pulse width varies from 0.5 ms to 2.5 ms for 0 and 180 degrees, respectively. In either case, the control pulse is positive and must be repeated every 20 ms. In many cases you will want the servo to be centered, for instance when used for steering. This is a control pulse width of 1.5 ms. I am using this reference and calling this 0 degrees. Shortening the control pulse width moves the servo in the negative direction (-degrees), CCW, or to the left (if you will). Lengthening the control pulse width moves the servo in the positive direction (+degrees), CW, or to the right.

To set the servo by degrees, we first need to change degrees to pulse width. The Degrees2us routine does this conversion by passing the degrees in the Temp register. The degree value is treated as a signed byte. Values of 128 or greater are negative, while values less than 128 are positive. In our case, the value is further limited to -90 to +90. If it is negative, we take the compliment and add 1 to get the absolute value. The span from 0 to -90 degrees is 1,000 µs, so 1,000 µs / 90 degrees = 11 µs/degree. So, we multiply the absolute value of Temp by 11 to get the negative deviation, and subtract it from 1,500 µs (centered, 0 degrees). For -90 degrees that would be 90 degrees × 11 µs/degree = 990 µs or 1,500 µs – 990 µs = 510 µs (close to minimum control pulse width.) If Temp is positive, just multiply it by 11 and add it to 1,500 µs. For 90 degrees that would be 90 degrees × 11 µs/degree = 990  µs or 1,500 µs + 990 µs = 2,490 µs (close to maximum control pulse width.) The result, in microseconds, is passed back in registers HEXH:LTimer5 has been initialized so its reload value is based in microseconds, which makes this a piece of cake.

The four sensor outputs are sampled automatically in pairs. These sensors are mounted externally (Figure 6) next to the solar array for a clear view of the sky. The ADC in this micro has special features that make it ideal for this project. Please refer to Figure 7 to see that the ADC has two sample and hold circuits, so two inputs essentially can be converted at the same time. A FIFO holds the conversion results for you, as the FIFO is four levels deep. Two sets of paired inputs can be sampled, converted, and stored to the FIFO in a single operation each time the GO bit is set in the ADCON0 register.

FIGURE 6 – The four light sensors are on a PCB that mounts to the bottom of the “X” shaped walls fitted into the clear dome. With one light sensor within its own walled quadrant, the module must be mounted along with the solar array and positioned so sensors are on the same axis as their respective servos.
FIGURE 7 – Twin analog mux and sample and hold circuits allow a pair of signals to be sampled in parallel and then converted and automatically saved in a FIFO. Two such operations can take place on different input pairs with one request.

The ADC interrupt has only to move the 10-bit values in the FIFO to each light sensor result register and set the FLAGS.ADC_Complete bit to signal the main loop that the conversion is complete and results have been stored. The main line code then adds these to the cumulative totals for each light sensor and repeats the conversion 63 more times. The totals are then divided by 64 to get an average for each light sensor. These values are then passed to the CalcPositionError routine to make all the decisions.

The CalcPositionError routine must set 2 of 6 bits in the Flags_ADC register. There are 3 bits, LT (<), EQ (=) or GT (>) for each axis S:N and W:E. First, we determine which sensor has the most light—the largest conversion value. To be considered equal, the difference between the two conversion values needs to be within some +/- tolerance value, and this is based on the largest conversion value. I’m using a tolerance of approximately 12% by dividing the largest conversion value by 8, shifting the value to the right 3 times. By comparing the tolerance value to the difference value, we can determine if the two conversion values are close enough to be considered equal, otherwise we already know which is greater. One of the 3 bits is set for that axis, and the process is repeated with the second axis.

The MoveServo1 and MoveServo2 don’t really move the servos; however, they are responsible for adjusting the NSDegrees and EWDegrees values, which are then converted into microsecond reload values for the control pulse width of the servos in the Timer0 interrupt. That’s where any change in movement occurs. Changes to the degrees value of each axis are based on the Flags_ADC bits from the CalcPositionError routine discussed above. While bits SEQN and WEQE indicate no change is necessary, SLTN and WLTE will increment the degrees value by 1, and SGTN and WGTE will decrement the degrees value by 1. Thus, the maximum change that can occur on an axis for each pass through the main loop is 1 degree.

My original idea was to use an RTC (real-time clock) chip. The circuit diagram is shown in Figure 8. Since this microcontroller has a built in low-frequency oscillator designed for this purpose, I altered my specs to use this and an interrupt routine to keep track of time of day. An external 32 kHz crystal is connected to feed this low-frequency oscillator. The oscillator is the clock for Timer1. It takes 32,768 cycles to equal 1 second. If Timer1 is reloaded with this value, the timer will rollover 1/second. I wanted to create a tick more often for 1/second, so I loaded Timer1 with 1/8 of 32,768 or 4,096. The interrupt generated uses several counters to keep track of different time periods, eighths, seconds, minutes, and hours. The Flags_Time register holds status bits for each time period that gets set after the RTC interrupt has reached the appropriate count for each.

FIGURE 8 – This original schematic included a connection for an external I2C RTC module. Because this MCU has support for a software RTC, the prototype does not include the hardware RTC. Although this lowers costs, I’m not convinced this is the right approach.

The time is kept in three registers—seconds, minutes and hours. These values (and others) must be converted into decimal digits to be displayed. I use a 16-bit to 5 decimal digits routine to handle word and byte values. This routine makes use of a single pre-written 16 x 16 bit unsigned division routine for the computations. I can just pop into the routine at various points to get a 5, 4, 3 or 2 digit conversion, depending on the values to convert. This is a simplified version of one that was written for signed values that handled displaying the negative sign and eliminating leading zeros.

Note that interrupts are serviced based on priority. Depending on which interrupts are active and where the interrupt of interest falls in that priority scheme, there may be a different number of cycles executed before that particular interrupt actually gets control. This means that reloading a register with a fixed value won’t be accurate unless you take into account the number of cycles that have elapsed since the interrupt was triggered. In many cases, this can be ignored, unless we are trying to attain absolute timing accuracy, as is in the case of RTC timing. To adjust for this, you must decrease your fixed value by some amount. You can get this amount from the timer’s value, which continues counting past the interrupt point (usually zero), and subtract it (and the time it takes to execute this adjustment) from your fixed value. That’s one hassle you can eliminate by using a hardware RTC.

I’m treating this project as a first-pass prototype. There are a few items that either haven’t been implemented, need improvement, or weren’t part of the initial feature list. From day one, I had planned on this project streaming data about the array power generation to the battery and load demand on it. The present circuitry does not touch on this at all. I’m still undecided about the best way to implement this. While I like a single-board solution, the modular approach has the flexibility of reuse. Consider the RTC, for example. Although I implemented it in the micro, I’m not convinced that a better approach would be to use an RTC module. Many use a simple interface, such as I2C, and include all external support parts, including a back-up battery. A module like this allows the date and time to be set externally and then plugged into the circuit. This reduces the need to add support code to the project to initialize it.

The use of servos for array movement was a basic requirement from the start. These have a much lower resistance to movement while un-powered than I had anticipated. My original thoughts were to un-power them between movements. However, the weight of the framed array, shown in the photos, was heavy enough to force rotation of the servos while un-powered. I suspect that keeping these powered adds an overhead that cost more power than the whole articulation arrangement is supposed to gain. Finding servos with increased gearing should eliminate this need. However, without power measurement circuitry, there is no proof, just an assumption.

You can see in Figure 9, that this month’s circuitry mounted within the waterproof enclosure still required additional circuity. Besides the battery and solar charging unit, you can see an additional PCB that handles the power regulation, including a switchable supply for the servos. You may recognize this from last month’s project. Maybe designing charging circuitry could include power measurement and regulation. Usually the charging circuitry is customized to the type of batteries being used. While the solar charging unit shown in the picture does some of this, feedback is non-existent, unless you want to push buttons and read its info on the LCD. For streaming/logging purposes I want these parameters accessible.

FIGURE 9 – This project’s PCB is mounted inside the water proof enclosure, seen in the bottom of this photo. The battery, solar charger and switchable regulators are not enclosed at this time. Replacement circuitry for the charger and power production/load power monitoring circuitry are still under development. Eventually, this will all be contained in a single enclosure.

That’s the engineer in me talking—never satisfied. I don’t want to overlook the positive results from this project. There are some engineering aspects I’m pleased with. The ability of the positioning system to cover a total hemisphere. Four-inch dryer hose fits over the servos to waterproof the mechanism yet allow flexibility of movement. A transparent dome enclosure for support of the light sensors. Waterproof enclosures with transparent latching doors (extra cost option, but adds that “cool” factor).

The LCD turned out to be an afterthought. Most projects include a debugging serial output to help monitor the execution of the program. It turns out that these data were so informative that I wanted them available full time. Using an LCD with a serial backpack allowed the streaming debug data to be displayed using the same serial connection. A few formatting commands added to the stream allowed positioning on the LCD.

I have to admit that setting this down anywhere and having it seek out the brightest spot in the sky was very pleasing. When I was testing this in my office, it would search out the fluorescent light in the ceiling. During the mornings, the sun coming through my window would drag the array away from the fluorescent, until the window lost its light source. At that point the array would leave the window and again seek the ceiling fixture.

I’ve finished reading Hidden Figures. I was hoping for some insight into how the space program accomplished its complex calculations prior to the electrical computer (not to be confused with the computers, the group responsible for mechanical computing algorithm results). While there were plenty of references, this book features the achievements of three African American women working at NASA at a time of gender and racial inequality—an historic walk along the parallel courses of social equality and technology. Next, I think I’ll read something a little lighter—maybe Clive Cussler. Quick, while there’s still sun and a hint of summer left, go read a book. Hammocks rule! 


Flash Microcontroller with High Performance PWM and A/D
Microchip Technology

Ambient Light Sensor
Everlight America
Toll Free: 844-352-6786

Serial LCD Board
Modern Device

Figure 2:  www.didel.commicrokitencoderEncoder.html

Everlight America |
Modern Device |
Microchip Technology |


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
Website | + posts

Jeff Bachiochi (pronounced BAH-key-AH-key) has been writing for Circuit Cellar since 1988. His background includes product design and manufacturing. You can reach him at: or at:

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Sun Tracking Project

by Jeff Bachiochi time to read: 15 min