Arduino Controlled Sun Tracking Solar Panel

So here it is, my Arduino controlled sun tracking solar panel. Ive spent the last few months building it and its been good fun. Im not going to bore you with details, you can email me or post a comment. Here are a pile of pictures, some brief descriptions of what I’ve done and most importantly, things that went wrong! It’s certainly not the most refined project, there’s a chuppachups container and some duct tape in there somewhere, NASA would be proud!

This is what the final product looks like, its mounted on my shed in the back garden and feeds a 96AH leisure battery through a solar charge controller(parts list at the bottom if your interested). The battery in turn feeds an inverter and also powers the Arduino and positioning motor(satellite dish linear actuator).

So there was no specific design, I just had a general idea of a frame in my head, watched a few videos of solar trackers on Youtube and rummaged around in the shed for some wood. Found a few lengths of 2×4, screwed them together so that the total outside width was the same as the solar panel. Of course I didn’t quite get that right the first time round. The triangle supports are just some cheap U-shaped aluminum, with a cross-brace to stop lateral movement of the wood frame and another aluminum brace to stop the panel moving laterally too.

Finally I added a 36v linear actuator that is designed for positioning satellite dishes, Ebay. This was not too difficult, though I had no idea how much movement I would need from the actuator. I just extended the actuator arm 50% out and mounted it on the frame so that the panel was flat, I figured that would give me equal movement in both directions. I then attached struts from the actuator arm to the panel at a length that would give the most movement without the panel bashing into the frame! This was not exact science.

I also waterproofed the frame with some leftover wood stain I used on my shed.

Now for some electronics!

The control mechanism is an arduino nano that is built onto a breadboard with a SN754410 quad half bridge and a 555 timer. The half bridge swaps the polarity output of power allowing the basic motor in the actuator to move in either direction based on the logic output from the ardiuno. The 555 timer deals with a reed sswitch in the actuator that engages as the linear screw turns, this allows indication when the actuator is moving, useful for detecting when the actuator is also at maximum or minimum extension. There are two 10k potentiometers that connect to 2 light dependent resistors allowing fine tuning of the light sensors once the frame is mounted on the shed roof. For detailed help there is a world of info on the arduino forum( this should get you started but a google search for “arduino dc motor control” should get you everything you need to know).

I was quite keen to understand what was happening and also have some control over the movement of the panel, not content with watching it move now and then I added a pile of LED’s and a few switches. The LEDs show the status of the controller and the switches allow me to change between auto and manual positioning and in manual mode I can move the panel one way or the other.

 

 There are 2 industrial limit switches on the frame that detect the movement limit of the panel in either direction and feed back to the arduino.

This is the chupachups tube, it used to hold 6 lollies, my girlfriend had those. I drilled a hole in the bottom and threaded the two light dependent resistors through and seperated them with a slice of cardboard glued in place. Then sealed it with duct tape, which has another technical advantage of blocking out light from passing through the thin plastic and confusing the sensors.

So thats pretty much it for the panel, with all that stuffed on top the shed I had to reinforce the roof a bit, nothing special, found a few more planks of wood and braced the roof where the frame sits.

This is the setup inside the shed, the panel feeds into a solar charge controller which takes care of charging the battery and provides a stabilized 12v out. That then powers the Arduino which in turn feeds the actuator motor. I know the motor is 36v but it works fine on 12v, its not exactly lifting a heavy load. I also have a 1kw modified sine-wave inverter that provides 240v, though its not quite enough to handle the lawn mower, or the hoover.

Parts List(not complete) pretty much all from Ebay!

  • Arduino nano v3
  • 2 x ip55 industrial limit switches
  • a few meters of U-shape aluminum
  • several meters of various electrical cable 4-core 2-core etc
  • sn754410 quad half bridge IC
  • 555 timer IC
  • 2×4 wood for frame
  • project box (Maplin)
  • LEDs, switches
  • lots of screws, a few bolts
  • 1 x 100w photovoltaic solar panel
  • solar charge controller generic 30A, there’s loads on Ebay

You might also want to view the Arduino code:

#include

dht DHT;

// =========
// = Debug =
// set to true to output extra debugging info,
// will fill up the logs pretty quick though, not recommended for deployment
const boolean debugOut = true;
// const boolean debugOut = false;

// =========
// ================
// = Digital pins =
// ================
// digital pin for DHT11
#define DHT11_PIN 4

// set pin numbers:
// const int ledPin = 13; // the number of the LED pin
const int motorPWM = 12; // pwm control speed H-Bridge pin 1,2EN
const int motor1 = 11; // motor out pins
const int motor2 = 10; // motor out pins

// movement limit switchs
const int limitSwitch1 = 2;
const int limitSwitch2 = 3;

// low light indicator(orange)
const int lowLightIndicatorLED = 9;

// Auto/manual
const int autoManualSwitch = 5; // auto = HIGH manual = LOW
const int autoManualMove1 = 6;
const int autoManualMove2 = 7;

// =================
// = Analogue pins =
// =================
// Analogue pins LDRs
const int ldrA0 = 0;
const int ldrA1 = 3;

// ================
// = general vars =
// ================
// light limits 0 - 1023
// const int equalLight = 800; not used
const int lowLight = 200; // if either sensor registeres less than $lowLight no movement occurs
const int tolerance = 50; // if the difference between each sensor is greater than $tolerance then reposition array

// movement resolution i.e. smalles amount of time between sensor readings while array is moving
const int motorMoveFrame = 500; // m'sec, more than 2 seconds could damage the array as it moves to close to the limit switches

// sub that sets the motor on/off bit on the H-Bridge, PWM
String motorOnOff = String("off"); // or on

int ldrReadingA0;
int ldrReadingA1;

void setup() {
// initialise motor pin out
pinMode(motor1, OUTPUT);
pinMode(motor2, OUTPUT);
// limit switches
pinMode(limitSwitch1, INPUT);
pinMode(limitSwitch2, INPUT);

// low light
pinMode(lowLightIndicatorLED, OUTPUT);

// Auto/Manual Switch
pinMode(autoManualSwitch, INPUT);
pinMode(autoManualMove1, INPUT);
pinMode(autoManualMove2, INPUT);

// create serial output
Serial.begin(9600);
Serial.println("Begin Solar panel project, DC Motor H-Bridge Driver, LDRs");
Serial.println("Motor is " + motorOnOff);

if(debugOut){
Serial.println("digitalRead(limitSwitch1):"); Serial.println(digitalRead(limitSwitch1));
Serial.println("digitalRead(autoManualSwitch):"); Serial.println(digitalRead(autoManualSwitch));
// Serial.println("lowLight:" + lowLight);
// Serial.println("tolerance:" + tolerance);
// Serial.println("motorMoveFrame:" + motorMoveFrame);

}

}
void loop(){
readSensors();

while (digitalRead(autoManualSwitch) == LOW){
Serial.println("**Manual Movement Switch Engaged**");
// Manual: check manual switch selection
if (digitalRead(autoManualMove1) == HIGH || digitalRead(autoManualMove2) == HIGH){
ManualMove();
}
delay(1000); // 0.5sec
}
// Auto: got enough light?
if (ldrReadingA0 > lowLight && ldrReadingA1 > lowLight){
digitalWrite(lowLightIndicatorLED,LOW);
moveArray();
}else{
Serial.println("Light level too low");
digitalWrite(lowLightIndicatorLED,HIGH);
}

// motor off
analogWrite(motorPWM, 0);
Serial.println("Motor off");

// read DHT11 temperature and humidity
readDHT11();

delay(500); // 0.5sec
}

void ManualMove(){

if (digitalRead(autoManualMove1) == HIGH){
motorForwards();
while(digitalRead(autoManualMove1) == HIGH && digitalRead(limitSwitch1) == LOW){
delay(motorMoveFrame);
readSensors();
}
analogWrite(motorPWM, 0);
Serial.println("Motor off");
}
if (digitalRead(autoManualMove2) == HIGH){
motorBackwards();
while(digitalRead(autoManualMove2) == HIGH && digitalRead(limitSwitch2) == LOW){
delay(motorMoveFrame);
readSensors();
}
analogWrite(motorPWM, 0);
Serial.println("Motor off");
}

}

void moveArray(){

if (ldrReadingA0 > ldrReadingA1){
// might need to move array..
if ((ldrReadingA0 - ldrReadingA1) > tolerance){
// yep, ldrReadingA0 is above tolerance so move it!
if (stillOkToMoveForwards()){ motorForwards(); }
// poop, this is too sensitive, need to move array so sensors match then exit "move" sub back to wider tollerance, other wise array will keep moving
// while((ldrReadingA0 - ldrReadingA1) > tolerance && digitalRead(limitSwitch1) == LOW && ldrReadingA0 > lowLight && ldrReadingA1 > lowLight){
while(stillOkToMoveForwards()){
delay(motorMoveFrame);
readSensors();
}
}
}
if (ldrReadingA0 < ldrReadingA1){ // might need to move array.. if ((ldrReadingA1 - ldrReadingA0) > tolerance){
// yep, ldrReadingA0 is above tolerance so move it!
if (stillOkToMoveBackwards()){ motorBackwards(); }
// while((ldrReadingA1 - ldrReadingA0) > tolerance && digitalRead(limitSwitch2) == LOW && ldrReadingA0 > lowLight && ldrReadingA1 > lowLight){
while(stillOkToMoveBackwards()){
delay(motorMoveFrame);
readSensors();
}
}
}
}
boolean stillOkToMoveForwards(){
if (ldrReadingA0 > ldrReadingA1
&& digitalRead(limitSwitch1) == LOW
&& ldrReadingA0 > lowLight
&& ldrReadingA1 > lowLight
&& (digitalRead(autoManualSwitch))){
if(debugOut){ Serial.println("stillOkToMoveForwards():true"); }
return true;

}
if(debugOut){ Serial.println("stillOkToMoveForwards():false"); }
return false;
}
boolean stillOkToMoveBackwards(){
if (ldrReadingA1 > ldrReadingA0
&& digitalRead(limitSwitch2) == LOW
&& ldrReadingA0 > lowLight
&& ldrReadingA1 > lowLight
&& (digitalRead(autoManualSwitch))){
if(debugOut){ Serial.println("stillOkToMoveBackwards():true"); }
return true;

}
if(debugOut){ Serial.println("stillOkToMoveBackwards():false"); }
return false;
}
void motorForwards(){
// movement limit in this direction reached?
if(digitalRead(limitSwitch1) == LOW){
analogWrite(motorPWM, 240);
Serial.println("Motor on, moving forwards");
digitalWrite(motor1, LOW);
digitalWrite(motor2, HIGH);
Serial.println("Motor pins 0 1");
}else{ Serial.println("--- limitSwitch1 engaged ---"); }
}
void motorBackwards(){
// movement limit in this direction reached?
if(digitalRead(limitSwitch2) == LOW){
analogWrite(motorPWM, 240);
Serial.println("Motor on, moving backwards");
digitalWrite(motor2, LOW);
digitalWrite(motor1, HIGH);
Serial.println("Motor pins 1 0");
}else{ Serial.println("--- limitSwitch2 engaged ---"); }
}

void readSensors(){
ldrReadingA0 = analogRead(ldrA0);
ldrReadingA1 = analogRead(ldrA1);
Serial.print("read ldr0=");
Serial.print(ldrReadingA0);
Serial.print(" read ldr1=");
Serial.println(ldrReadingA1);
}
void readDHT11(){
// READ DATA, min 2 secs due to sensor output constraints
Serial.print("DHT11\t");
int chk = DHT.read11(DHT11_PIN);
switch (chk)
{
// case 0: Serial.print("OK,\t"); break;
case 0: break; // ok? don't print anything
case -1: Serial.print("Checksum error,\t"); break;
case -2: Serial.print("Time out error,\t"); break;
default: Serial.print("Unknown error,\t"); break;
}
// DISPLAT DATA
Serial.print("DHT.humidity\%=\"");
Serial.print(DHT.humidity,1);
Serial.print("\"\tDHT.temperature (c)=\"");
Serial.print(DHT.temperature,1);
Serial.println("\"");

delay(2000);
}