Click here to Skip to main content
15,346,923 members
Articles / Internet of Things / Arduino
Posted 22 Sep 2010


26 bookmarked

Arduino Platform - Differential Gap Control (Solar Tracker)

Rate me:
Please Sign up or sign in to vote.
4.95/5 (21 votes)
22 Sep 2010CPOL4 min read
Simple implementation of a differential gap control to track the sun East/West movement



This article gives a very simple introduction into writing a differential gap controller on the Arduino platform. I have used a Duemilanove for this example.

This project consists of two inputs which track light levels on the East/West which are then used to move a servo, rotating East/West tracking the strongest light level.

The crude prototype in all its glory......the surplus MSDN discs have found a use at last:


How It Works

The two light sensors are basic photoresistors, these are mounted at 90' to each other, imagine these are your eyes, if you look straight ahead at a wall, the left eye would point 45' to the left, and the right eye 45' to the right. These sensors are also shielded, so they will see the brightest light levels when pointing directly at the light source. If the light source then moves, a shadow is cast onto the sensor, changing its resistance.

When tracking the sunlight, e.g. for a solar panel, you would want the maximum sunlight intensity, to achieve this, both sensors would therefore need to see the same intensity of light. This is how we determine the input for the gap controller.

Read both of the input sensor values, and do a comparison, 0 difference means they are at the same light level, a -ve error value means the light is brighter to the right, and a +ve error value means the light is brighter to the left.

The servo is then sent up with a position value, and we simply increment or decrement the output on each scan to rotate the platform east and west to find the optimum balance light levels on the sensors again.

In the code, there are also upper and lower limits to prevent damage to the servo by driving hard against its end stops. A deadband value is also established in the code. This effectively means the output will not change, unless the error between the two inputs is greater than a certain value. The deadband prevents jitter and constant twitching of the sensor.

I have also added a very basic 2 point average to help smooth out spikes in the input sensors. In reality, you might want to filter this out further to filter out unwanted noise or spikes.

Using the Code

The sketch file provided in the download can be uploaded onto the Arduino. Let's take a look at the code in a bit more detail now.

The first part of the code is used to establish the IO Pin allocation, the variables for holding the input readings, the error and the rolling error average. The deadband range is also defined, as well as the upper and lower limits for the servo, and also the initial start point for the servo.

The #include statement makes reference to a prebuilt library for handling servo's on the Arduino. It basically allows a simple value to be written out to the servo object, and then takes care of the Pulse Width Modulation used to set the servo position.

#include <servo.h>

//IO Pins
int pinL = 5;              //Left Sensor IO Pin
int pinR = 4;              //Right Sensor IO Pin
int pinServo = 11;         //Servo PWM pin

int leftValue = 0;         //The left Sensor Value
int rightValue = 0;        //The right Sensor Value
int error =0;              //The Deviation between the 2 sensors
int errorAVG = 0;          //Error Average - Rolling 2 Point

int deadband = 10;         //Range for which to do nothing with output 10 = -10 to +10  
//Servo Stuff
Servo hServo;              //The servo object
int Position = 45;         //Position to write out

int minPos = 5;            //Min Position
int maxPos = 150;          //Max Position

float output = (maxPos - minPos) /2;  //Initial output Position

The next part of the code is the Arduino Setup method. This runs once and is effectively used to initialise anything you want before the main code loop executes. In this example, all I am doing is setting the Servo output to Min, Max and MidPoint for 5 Seconds each, to allow any positioning of the hardware on my desk for testing. The Serial statements just pump messages out the serial port and can be monitored on the PC.

void setup()


//Set Servo to Centre for Alignment Purpose
Serial.println("Moving Servo to Minimum Position");
Serial.println("Moving Servo to Maximum Position");
Serial.println("Moving Servo to Mid-Point");
Serial.println("Going Live................");

The final part of the code is the main loop body, this is the loop that will run continuously until power is switched off or new code is downloaded to the Arduino.

The input values are first read, then some debug info is pumped out to the serial port. The error values are calculated, and the revised new position for the sensor is determined by adding the value returned by getTravel(). The limits are also checked to ensure we do not exceed these.

void loop()
  //Input Reading
   leftValue = analogRead(pinL);
   rightValue = analogRead(pinR);

 Serial.print("L = "); Serial.print(leftValue); Serial.print(" | ");
 Serial.print("R = "); Serial.print(rightValue); Serial.print(" | ");
 Serial.print("E = "); Serial.print(error); Serial.print(" | ");
 Serial.print("Eavg = "); Serial.print(errorAVG); 

 error = leftValue - rightValue;
 errorAVG = (errorAVG + error) / 2;
 float newOutput = output + getTravel();
 if (newOutput > maxPos)
   Serial.println("At Upper Limit");
   newOutput = maxPos;
   if (newOutput < minPos)
     Serial.println("At Lower Limit");
     newOutput = minPos;
    Serial.println("Writing output");
    //Output Writing
    output = newOutput;

I also have a helper method getTravel() which is used to determine if I need to rotate left, rotate right or do nothing (e.g. within deadband) on each scan. It simply returns a +1, -1 or 0 which is then added to the current position before being written out to the servo.

int getTravel()
  // -1 = Left; +1 = Right
 if (errorAVG < (deadband * -1))
   return 1;
   if (errorAVG > deadband)
     return -1;
     //Do not move within deadband
     return 0;

The Working Prototype

A video of the prototype running can be found here.

Points of Interest

This is as simple as it gets. Ways that you could enhance this are:

  • Implement an improved noise filtering on the input signals
  • Add some form of PID (Proportional/Integral/Derivative) to the control algorithm
  • Add a second servo and additional sensors for Vertical motion

What Else

Visit my other articles or my website for more Arduino bits and bobs. Search CodeProject for Arduino, there are also more by others.


  • 22nd September 2010 - First version of article


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Scotland Scotland
I have been working in the Oil & Gas Industry for over 30 years now.

Core Discipline is Instrumentation and Control Systems.

Completed Bsc Honours Degree (B29 in Computing) with the Open University in 2012.

Currently, Offshore Installation Manager in the Al Shaheen oil field, which is located off the coast of Qatar. Prior to this, 25 years of North Sea Oil & Gas experience.

Comments and Discussions

GeneralMy vote of 5 Pin
Gun Gun Febrianza10-Jun-16 3:47
Member Gun Gun Febrianza10-Jun-16 3:47 
Questionprogram solar trecker Pin
Member 1208282726-Oct-15 20:33
MemberMember 1208282726-Oct-15 20:33 
AnswerRe: program solar trecker Pin
DaveAuld27-Oct-15 0:34
professionalDaveAuld27-Oct-15 0:34 
QuestiongetTravel() Pin
Value995-May-13 6:38
MemberValue995-May-13 6:38 
AnswerRe: getTravel() Pin
DaveAuld5-May-13 8:36
professionalDaveAuld5-May-13 8:36 
QuestionServo angle seeting Pin
Member 99507974-May-13 10:38
MemberMember 99507974-May-13 10:38 
AnswerRe: Servo angle seeting Pin
DaveAuld4-May-13 22:27
professionalDaveAuld4-May-13 22:27 
GeneralRe: Servo angle seeting Pin
Member 99507975-May-13 6:31
MemberMember 99507975-May-13 6:31 
GeneralerrorAvG Pin
Member 99507974-May-13 10:09
MemberMember 99507974-May-13 10:09 
GeneralRe: errorAvG Pin
DaveAuld4-May-13 22:25
professionalDaveAuld4-May-13 22:25 
GeneralRe: errorAvG Pin
Member 99507975-May-13 6:31
MemberMember 99507975-May-13 6:31 
Questionservo keeps moving Pin
Member 995079727-Apr-13 9:07
MemberMember 995079727-Apr-13 9:07 
AnswerRe: servo keeps moving Pin
DaveAuld27-Apr-13 23:03
professionalDaveAuld27-Apr-13 23:03 
GeneralRe: servo keeps moving Pin
Member 995079730-Apr-13 16:57
MemberMember 995079730-Apr-13 16:57 
GeneralRe: servo keeps moving Pin
Member 995079730-Apr-13 16:58
MemberMember 995079730-Apr-13 16:58 
GeneralRe: servo keeps moving Pin
DaveAuld30-Apr-13 20:36
professionalDaveAuld30-Apr-13 20:36 
GeneralRe: servo keeps moving Pin
Member 99507971-May-13 16:14
MemberMember 99507971-May-13 16:14 
GeneralRe: servo keeps moving Pin
DaveAuld1-May-13 22:30
professionalDaveAuld1-May-13 22:30 
GeneralRe: servo keeps moving Pin
Member 99507974-May-13 9:42
MemberMember 99507974-May-13 9:42 
GeneralRe: servo keeps moving Pin
DaveAuld4-May-13 10:05
professionalDaveAuld4-May-13 10:05 
GeneralRe: servo keeps moving Pin
Member 99507974-May-13 10:06
MemberMember 99507974-May-13 10:06 
GeneralRe: servo keeps moving Pin
DaveAuld4-May-13 10:08
professionalDaveAuld4-May-13 10:08 
GeneralRe: servo keeps moving Pin
Member 99507974-May-13 10:09
MemberMember 99507974-May-13 10:09 
GeneralRe: servo keeps moving Pin
DaveAuld30-Apr-13 21:55
professionalDaveAuld30-Apr-13 21:55 
Questionphotoresistor Pin
Member 995079728-Mar-13 19:02
MemberMember 995079728-Mar-13 19:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.