Another Dual Axis Solar Tracker

Hi all,

I'm building a mini dual axis solar tracker which will be up scaled to full size once I have this code working as required.
For a start I'm using an Arduino Nano clone but will most likely upgrade to a Mega when I run out of pins on the nano and need a reliable micro processor. I'm using mini gear motors for actuators, a Pololu dual motor driver and 4 LDR sensors + 4 10K resistors.

This is just the start of the sketch and I plan on introducing limit switches, LED's and a wind sensor for safety after I get past what I'm stuck on now.

As per usual I found a sketch that looks close to what I want and modified it to suit my needs.

What I'd like to implement on this sketch is a timed "MotorSeek", say... 2 sec where the motors are aloud to set the position of the LDR's directly facing the sun.
Then a timed "MotorStop" for about 6 sec where the motors are shut off.
Then 2 sec for adjustment and 6 sec switch off, and so on.
In the full scale version these times would be more like 1 min Seek time and 20-30 mins Stop time.

I would like to use "millis" NOT delay. I don't no where to insert this line or if indeed it's the correct line to useif ((unsigned long)(currentMillis - previousMillisSeek) >= MotorsSeek) and or if ((unsigned long)(currentMillis - previousMillisStop) >= MotorsStop)

Here is what I have so far.

/* Dual axis solar tracker using 12v DC motor linier actuators,
4 LDR's, 4 limit switches and a wind sensor */


//LDR PINS
const int LeftTopLDR = A0;
const int RightTopLDR = A1;
const int LeftBottomLDR = A2;
const int RightBottomLDR = A3;

//MOTOR DRIVER PINS
const int AIN1 = 4;
const int AIN2 = 5;
const int APWM = 6;
const int BIN1 = 7;
const int BIN2 = 8;
const int BPWM = 9;
const int StandBy = 10;

//LED PINS
const int MovementLimitLED = 3;

//WIND SENSOR
const int WindSensor = 13;

//LIMIT SWITCH PINS
const int HorizontalLimitSwitch1 = 11;
const int HorizontalLimitSwitch2 = 12; 
const int VerticalLimitSwitch1 = 1;
const int VerticalLimitSwitch2 = 2;

//MOTORS SEEK AND STOP VARIABLES
unsigned long previousMillisStop = 0;
unsigned long previousMillisSeek = 0;
unsigned long motorsStop = 6000;
unsigned long motorsSeek = 2000;

void setup()
{
    Serial.begin(9600);
    
    pinMode(AIN1, OUTPUT);
    pinMode(AIN2, OUTPUT);
    pinMode(BIN1, OUTPUT);
    pinMode(BIN2, OUTPUT);
    
    pinMode(APWM, OUTPUT);
    pinMode(BPWM, OUTPUT);
    pinMode(StandBy, OUTPUT);
    
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    
    digitalWrite(APWM, LOW);
    digitalWrite(BPWM, LOW);
    digitalWrite(StandBy, LOW);
}
void loop()
{
  unsigned long currentMillis = millis();
//  LimitReset();
//  FlatMode();
//  Debug();

//LDR's READ AND CALCULATE    
  int lt = analogRead(LeftTopLDR); 
  int rt = analogRead(RightTopLDR); 
  int lb = analogRead(LeftBottomLDR); 
  int rb = analogRead(RightBottomLDR); 
  
  int avt = (lt + rt) / 2; // average value top
  int avb = (lb + rb) / 2; // average value bottom
  int avl = (lt + lb) / 2; // average value left
  int avr = (rt + rb) / 2; // average value right
  int dvert = avt - avb; // check the diffirence of top and bottom
  int dhoriz = avl - avr;// check the diffirence of left and rigt
  
  int tolerance = 10;
  
// check if the diffirence is in the tolerance else change vertical angle  
 if (-1*tolerance > dvert || dvert > tolerance) 
 {
  if (avt > avb)
  {
    digitalWrite(StandBy, HIGH); 
    digitalWrite(AIN1, HIGH);
    digitalWrite(AIN2, LOW);
    analogWrite(APWM, 180); 
  }
  else if (avt < avb)
  {
    digitalWrite(StandBy, HIGH); 
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, HIGH);
    analogWrite(APWM, 180);
  }
   else if (avt = avb)
  {
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(APWM, LOW);
  }
 
// check if the diffirence is in the tolerance else change horizontal angle 
 if (-1*tolerance > dhoriz || dhoriz > tolerance) 
 {
  if (avl > avr)
  {
    digitalWrite(StandBy, HIGH); 
    digitalWrite(BIN1, HIGH);
    digitalWrite(BIN2, LOW);
    analogWrite(BPWM, 180);
  }
 else if (avl < avr)
  {
    digitalWrite(StandBy, HIGH); 
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, HIGH);
    analogWrite(BPWM, 180); 
  }
 else if (avl = avr)
  {
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    digitalWrite(BPWM, LOW);
  }
 }
 }
}

By breaking your code into several short single-purpose functions it will be much easier to develop, test, debug and maintain. See Planning and Implementing a Program

If you create a function called (say) updatePosition() you can call that whenever you want to. Something like

if (millis() - prevMoveMillis >= moveIntervalMillis) {
   updatePosition()
}

or you could set a flag like this

if (millis() - prevMoveMillis >= moveIntervalMillis) {
   updateNow = true
}

and in the updatePosition() function you could start with

void updatePosition() {
   if (updateNow == false) {
      return;  // do nothing
   }
   // all the other position code
}

If you really need to set a timeout on the move then the second version might be simplest because you could include within updatePosition() some code to set updateNow back to false after a certain time

However I suspect (because the sun is slow) that you could track it without any of that complication.

...R

as an alternative to what you asked for.
we know that the sun never reverses it's course across the sky.
therefore we can predict with total certainty that if you ever motor your array to a point past the current sun location, you know that in short order the sun's path with be back in center, then be behind your sensor requiring a forward movement.

if your motor speed, and your gearing are set to be close to the speed of the sun, your overshoot will not be extensive.

also, if you use a simple integral, you can 'step' the motors. again, if you overshoot, just wait.

if the leading sensor - trailing senor you could put that into a formula
error = LS - TS

if you check that every second, then you could get a cumulative value that will grow each iteration.
once it reachs some threshold you pick, then re-set to 0 and move the motor on that axis.

That has got my brain working again. I kind of thought a restructure was in order just didn't know where to start. I'll go through your link today Robin and find the best way to implement your suggestions. Dave I like your idea of the accumulative value. Will try out some code tonight.

Thanks

J

Wowsa, It's taken me all week and many versions but I think this one might suit my needs. I'm not sure if it will work because I think it needs a deadzone to

else if (avl = avr){
else if (avt = avb){

in order for the motors to switch LOW. If I run the sketch in the OP the motors are constantly jogging back and forth when the tracker is close to alignment but never digitalWrite LOW

For now all I want the motors to do is.... when both motorA and motorB states are false then stopInterval is triggered and the motors are off for 6sec. After 6sec the motor states change to true and the updatePosition() function is run.

When motorA has aligned with the sun then motorA digitalWrites LOW and the motorAstate is set to false leaving motorB to finish it's alignment before switching LOW and motorBstate to false

Once both motor states are false the stopInterval is triggered again.

/* Dual axis solar tracker using 12v DC motor linier actuators,
4 LDR's, 4 limit switches and a wind sensor */


//LDR PINS
const int LeftTopLDR = A0;
const int RightTopLDR = A1;
const int LeftBottomLDR = A2;
const int RightBottomLDR = A3;

//MOTOR DRIVER PINS
const int AIN1 = 4;
const int AIN2 = 5;
const int APWM = 6;
const int BIN1 = 7;
const int BIN2 = 8;
const int BPWM = 9;
const int StandBy = 10;

//LED PINS
const int MovementLimitLED = 3;

//WIND SENSOR
const int WindSensor = 13;

//LIMIT SWITCH PINS
const int HorizontalLimitSwitch1 = 11;
const int HorizontalLimitSwitch2 = 12; 
const int VerticalLimitSwitch1 = 1;
const int VerticalLimitSwitch2 = 2;

//MOTOR FLAGS AND STOP VARIABLES
boolean motorAstate = false;
boolean motorBstate = false;
unsigned long prevStopMillis = 0;
unsigned long stopInterval = 6000;


void setup()
{
    Serial.begin(9600);
    
    pinMode(AIN1, OUTPUT);
    pinMode(AIN2, OUTPUT);
    pinMode(BIN1, OUTPUT);
    pinMode(BIN2, OUTPUT);
    
    pinMode(APWM, OUTPUT);
    pinMode(BPWM, OUTPUT);
    pinMode(StandBy, OUTPUT);
    
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    
    digitalWrite(APWM, LOW);
    digitalWrite(BPWM, LOW);
    digitalWrite(StandBy, LOW);
}
void loop(){
  
  unsigned long currentMillis = millis();
  
if(motorAstate && motorBstate == false){  
if((unsigned long)currentMillis - prevStopMillis >= stopInterval){
  motorAstate = true;
  motorBstate = true;
 }
}
if(motorAstate && motorBstate == true){ 
   updatePosition();
 }
}
void updatePosition(){

//LDR's READ AND CALCULATE    
  int lt = analogRead(LeftTopLDR); 
  int rt = analogRead(RightTopLDR); 
  int lb = analogRead(LeftBottomLDR); 
  int rb = analogRead(RightBottomLDR); 
  
  int avt = (lt + rt) / 2; // average value top
  int avb = (lb + rb) / 2; // average value bottom
  int avl = (lt + lb) / 2; // average value left
  int avr = (rt + rb) / 2; // average value right
  int dvert = avt - avb; // check the diffirence of top and bottom
  int dhoriz = avl - avr;// check the diffirence of left and rigt
  
  int tolerance = 100;
  
// check if the diffirence is in the tolerance else change vertical angle  
 if (-1*tolerance > dvert || dvert > tolerance){
  if (avt > avb){
    digitalWrite(StandBy, HIGH); 
    digitalWrite(AIN1, HIGH);
    digitalWrite(AIN2, LOW);
    analogWrite(APWM, 180); 
  }
  else if (avt < avb){
    digitalWrite(StandBy, HIGH); 
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, HIGH);
    analogWrite(APWM, 180);
  }
   else if (avt = avb){
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(APWM, LOW);
    motorAstate = false;
  }
// check if the diffirence is in the tolerance else change horizontal angle 
 if (-1*tolerance > dhoriz || dhoriz > tolerance){
  if (avl > avr){
    digitalWrite(StandBy, HIGH); 
    digitalWrite(BIN1, HIGH);
    digitalWrite(BIN2, LOW);
    analogWrite(BPWM, 180);
  }
 else if (avl < avr){
    digitalWrite(StandBy, HIGH); 
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, HIGH);
    analogWrite(BPWM, 180); 
  }
 else if (avl = avr){
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    digitalWrite(BPWM, LOW);
    motorBstate = false;
  }
 }
 }
}
   else if (avt = avb){
   else if (avl = avr){

Ups.

In this line

if (-1*tolerance > dvert || dvert > tolerance){

can someone please brake it down explain what/how it does? Specifically this part

-1*tolerance

I didn't write this part of the sketch so I'm not sure how it works. I'm thinking I could reverse it to help switch the motors off when they're in position.

Thanks,
J

work with real numbers.

tolerance = 10;

-1*tolerance = -10

how does your equation work when you use -10 ?

you have an if / else / else statement

if your tracking it behind, dvert > tollerance, / move forward
else
if your tracking is too far ahead (-tollerance > dvert) / move back
else
baby bear

as an operational comment, if you have PV cells, you will be able to monitor the output.
if your output is 5% lower at the point you adjust, you might want to narrow your tolerance.

you have to weigh the cost in power to move the array vs. the energy gained by keeping it on point.

Thanks for that Dave. The tolerance makes sense now.

Turns out the reason it wasn't stopping is this piece was following the wrong if statement. So I just dropped it down a set of braces and got rid of the (avr = avl) & the (avt = avb) parts

   else{
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(APWM, LOW);
 }

Now the tracker will stop tracking, however if the light source is more than a few degrees out of alignment it will start to move in increasingly bigger circles. If I were to start the tracker say... 45° out of alignment, it seems to want to go the opposite way to what it should. I'm testing this using a lamp but will double check tomorrow on the sun. Here is the current sketch.

//LDR PINS
const int LeftTopLDR = A0;
const int RightTopLDR = A1;
const int LeftBottomLDR = A2;
const int RightBottomLDR = A3;

//MOTOR DRIVER PINS
const int AIN1 = 4;
const int AIN2 = 5;
const int APWM = 6;
const int BIN1 = 7;
const int BIN2 = 8;
const int BPWM = 9;
const int StandBy = 10;

//LED PINS
const int MovementLimitLED = 3;

//WIND SENSOR
const int WindSensor = 13;

//LIMIT SWITCH PINS
const int HorizontalLimitSwitch1 = 11;
const int HorizontalLimitSwitch2 = 12; 
const int VerticalLimitSwitch1 = 1;
const int VerticalLimitSwitch2 = 2;

//MOTORS MOVE AND STOP VARIABLES
boolean updateNow = false;
unsigned long prevStopMillis = 0;
unsigned long prevMoveMillis = 0;
unsigned long stopInterval = 6000;
unsigned long moveInterval = 2000;

void setup()
{
    Serial.begin(9600);
    
    pinMode(AIN1, OUTPUT);
    pinMode(AIN2, OUTPUT);
    pinMode(BIN1, OUTPUT);
    pinMode(BIN2, OUTPUT);
    
    pinMode(APWM, OUTPUT);
    pinMode(BPWM, OUTPUT);
    pinMode(StandBy, OUTPUT);
    
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    
    digitalWrite(APWM, LOW);
    digitalWrite(BPWM, LOW);
    digitalWrite(StandBy, LOW);
}
void loop(){

//LDR's READ AND CALCULATE    
  int lt = analogRead(LeftTopLDR); 
  int rt = analogRead(RightTopLDR); 
  int lb = analogRead(LeftBottomLDR); 
  int rb = analogRead(RightBottomLDR); 
  
  int avt = (lt + rt) / 2; // average value top
  int avb = (lb + rb) / 2; // average value bottom
  int avl = (lt + lb) / 2; // average value left
  int avr = (rt + rb) / 2; // average value right
  int dvert = avt - avb; // check the diffirence of top and bottom
  int dhoriz = avl - avr;// check the diffirence of left and rigt
  
  int tolerance = 10;
  
  Serial.print("average top ");
  Serial.print(avt);
  Serial.print("  average bottom ");
  Serial.print(avb);
  Serial.print("  average left ");
  Serial.print(avl);
  Serial.print("  average right ");
  Serial.print(avr);
  Serial.print("  dvert ");
  Serial.print(dvert);
  Serial.print("  dhoriz ");
  Serial.println(dhoriz);
  
// check if the diffirence is in the tolerance else change vertical angle  
 if(-1*tolerance > dvert || dvert > tolerance){
   digitalWrite(StandBy, HIGH);
  if(avt > avb){
    digitalWrite(AIN1, HIGH);
    digitalWrite(AIN2, LOW);
    analogWrite(APWM, 180); 
  }
  else if(avt < avb){
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, HIGH);
    analogWrite(APWM, 180);
  }
 }
   else{
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(APWM, LOW);
 }
 
// check if the diffirence is in the tolerance else change horizontal angle 
 if(-1*tolerance > dhoriz || dhoriz > tolerance){
   digitalWrite(StandBy, HIGH); 
  if(avl > avr){
    digitalWrite(BIN1, HIGH);
    digitalWrite(BIN2, LOW);
    analogWrite(BPWM, 180);
  }
 else if(avl < avr){
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, HIGH);
    analogWrite(BPWM, 180); 
  }
 }
 else{
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    digitalWrite(BPWM, LOW);
 }
}

The other issue is when I try to add the time out the states never change to true? This is the sketch with the time out

//LDR PINS
const int LeftTopLDR = A0;
const int RightTopLDR = A1;
const int LeftBottomLDR = A2;
const int RightBottomLDR = A3;

//MOTOR DRIVER PINS
const int AIN1 = 4;
const int AIN2 = 5;
const int APWM = 6;
const int BIN1 = 7;
const int BIN2 = 8;
const int BPWM = 9;
const int StandBy = 10;

//LED PINS
const int MovementLimitLED = 3;

//WIND SENSOR
const int WindSensor = 13;

//LIMIT SWITCH PINS
const int HorizontalLimitSwitch1 = 11;
const int HorizontalLimitSwitch2 = 12; 
const int VerticalLimitSwitch1 = 1;
const int VerticalLimitSwitch2 = 2;

//MOTOR FLAGS AND STOP VARIABLES
boolean motorAstate = false;
boolean motorBstate = false;
unsigned long prevStopMillis = 0;
unsigned long stopInterval = 6000;

int tolerance = 5;

void setup()
{
    Serial.begin(9600);
    
    pinMode(AIN1, OUTPUT);
    pinMode(AIN2, OUTPUT);
    pinMode(BIN1, OUTPUT);
    pinMode(BIN2, OUTPUT);
    
    pinMode(APWM, OUTPUT);
    pinMode(BPWM, OUTPUT);
    pinMode(StandBy, OUTPUT);
    
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    
    digitalWrite(APWM, LOW);
    digitalWrite(BPWM, LOW);
    digitalWrite(StandBy, LOW);
}
void loop(){
  
  unsigned long currentMillis = millis();
  
if(motorAstate && motorBstate == false){ 
  digitalWrite(StandBy, LOW);  
 if((unsigned long)currentMillis - prevStopMillis >= stopInterval){
  motorAstate = true;
  motorBstate = true;
  updatePosition();
//  prevStopMillis = currentMillis;
  }
 }
}
void updatePosition(){

//LDR's READ AND CALCULATE    
  int lt = analogRead(LeftTopLDR); 
  int rt = analogRead(RightTopLDR); 
  int lb = analogRead(LeftBottomLDR); 
  int rb = analogRead(RightBottomLDR); 
  
  int avt = (lt + rt) / 2; // average value top
  int avb = (lb + rb) / 2; // average value bottom
  int avl = (lt + lb) / 2; // average value left
  int avr = (rt + rb) / 2; // average value right
  int dvert = avt - avb; // check the diffirence of top and bottom
  int dhoriz = avl - avr;// check the diffirence of left and rigt
  
  Serial.print("average top ");
  Serial.print(avt);
  Serial.print("  average bottom ");
  Serial.print(avb);
  Serial.print("  average left ");
  Serial.print(avl);
  Serial.print("  average right ");
  Serial.print(avr);
  Serial.print("  dvert ");
  Serial.print(dvert);
  Serial.print("  dhoriz ");
  Serial.println(dhoriz);
  
// check if the diffirence is in the tolerance else change vertical angle  
 if(-1*tolerance > dvert || dvert > tolerance){
   digitalWrite(StandBy, HIGH);
  if(avt > avb){
    digitalWrite(AIN1, HIGH);
    digitalWrite(AIN2, LOW);
    analogWrite(APWM, 180); 
  }
  else if(avt < avb){
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, HIGH);
    analogWrite(APWM, 180);
  }
 }
   else{
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(APWM, LOW);
    motorAstate = false;
  }
  
// check if the diffirence is in the tolerance else change horizontal angle 
 if(-1*tolerance > dhoriz || dhoriz > tolerance){
   digitalWrite(StandBy, HIGH); 
  if(avl > avr){
    digitalWrite(BIN1, HIGH);
    digitalWrite(BIN2, LOW);
    analogWrite(BPWM, 180);
  }
 else if(avl < avr){
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, HIGH);
    analogWrite(BPWM, 180); 
  }
 }
 else{
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    digitalWrite(BPWM, LOW);
    motorBstate = false;
 }
}

Well turns out that my idea of horizontal and vertical motors were different to the trackers H and V. Switched the motor wires over and it seems to be tracking the sun quite well now.

Just need to figure out the timing thing now. And the rest of the code I guess, but one step at a time eh.

Hi guy's.

I got the sketch pretty much to where I want it however it has a bug I can't get rid of. I'm not sure why it does it because in my eyes it shouldn't be able to do it. I'll do my best to explain the fault.

If the wind is to strong or there's not enough light, the tracker is sent into lie flat mode where the tracker will lie flat and level. This is a safety feature that minimizes surface area during high winds or gets the tracker ready for the next morning in normal conditions.

Once the tracker is lying flat a timer is run so that it stays flat for a set amount of time. I put this in place so after a gust of wind the tracker wouldn't lie flat then go straight back to looking for the sun. (else on a windy day it would spent the entire day lying flat then searching then lying flat then searching...)

This is the piece if code that should take care of that

  if((unsigned long)currentLieFlatMillis - prevLieFlatMillis >= lieFlatInterval){
     lieFlatTrigger = false;
     vertFlat = false;
     horizFlat = false;
     Serial.print("   PING !!!!!!!!!!!!!          ");
     prevLieFlatMillis = currentLieFlatMillis;
  }

The void loop() looks like this

void loop(){
   
   CheckLight();   //CHECK WIND AND LIGHT CONDITIONS:
   CheckWind();
   
   if(lieFlatTrigger == true){
      LieFlat();                       //MOVE PANELS SO THEY ARE FLAT AND LEVEL FOR 
    //  Serial.print("  Lie Flat ");  //ETHER HIGH WINDS OR LOW LIGHT (NIGHT TIME).
   }
   else if((LightState == true) && (WindSpeed == true) && (lieFlatTrigger == false)){
      UpdatePosition();                       //ONLY WHEN ALL CONDITIONS ARE 
    //  Serial.print("   UpdatePosition  ");  //MET IS IT OK TO UPDATE POSITION. 
   }
}

If I set the wind speed to above the maxWind or light below minLight for a second (just enough to set the lieFlatTrigger = true) the tracker will move to the lie flat position.
For some reason the lieFlatTrigger is set to false before the lieFlatInterval has run and I've noticed that I get a single "ping!!!!" as soon as the tracker reaches the lie flat position so sometimes it returns to searching for the sun (updatePosition())straight away, other times it will stay lying flat for a while but it's rear for it to actually stay lying flat for the entire duration of the lieFlatInterval.
If I increase the lieFlatInterval to say 2 minutes it lies flat for longer but still not the duration.

I have tried a hundred different ways of writing it. All but one failed to fix the problem.
The only way I got consistent results was to put a dirty old delay() in there but then my LED doesn't fade off and on :cry: . The fading LED is not a biggy but it would be pleasing to get to the bottom of it.

If the wind is held higher than max or light held lower than min the tracker will stay lying flat till after the first interval has pasted, if the weather conditions return to normal, any time after the first lieFlatInterval has past it will stay lying flat for the correct remainder of the lieFlatInterval before returning to searching for the sun as it should.

Any help would be most appreciated.

I'll post the whole sketch in the next post.

Will have to be more that one post.

/* Dual axis solar tracker using 12v DC motor linier actuators,
4 LDR's, 4 limit switches and a wind sensor */


//LDR 
const int LeftTopLDR = A0;
const int RightTopLDR = A1;
const int LeftBottomLDR = A2;
const int RightBottomLDR = A3;
int lt = 0;
int rt = 0;
int lb = 0;
int rb = 0;
int avt = 0;
int avb = 0;
int avl = 0;
int avr = 0;
int dvert = 0;
int dhoriz = 0;
const int minLight = 500;
boolean LightState = true;

//MOTOR DRIVER 
const int AIN1 = 4;
const int AIN2 = 5;
const int APWM = 6;
const int BIN1 = 7;
const int BIN2 = 8;
const int BPWM = 9;
const int StandBy = 10;
boolean motorAstate = true;
boolean motorBstate = true;

//LED
const int redLEDpin = 3;
const int greenLEDpin = 12;
const int blueLEDpin = 11;
int slowFlash = 3000;
int displace = 500;
int value;
long time = 0;

//WIND SENSOR
boolean WindSpeed = true;
const int WindSensor = A6;
const int maxWind = 600;

//POTENTIOMETERS
const int vertPotPin = A4;
const int horizPotPin = A5;
const int vertPotMax = 510;
const int vertPotMin = 300;
const int horizPotMax = 750;
const int horizPotMin = 300;
const int horizPotMid = 510;
const int deadBand = 10;
const int numReadings = 10;             // this value determines the size of the readings array
int vertReadings[numReadings];          // the readings from the analog input
int vertIndex = 0;                      // the index of the current reading
int vertTotal = 0;                      // the running total
int vertAverage = 0;
int horizReadings[numReadings];         // the readings from the analog input
int horizIndex = 0;                     // the index of the current reading
int horizTotal = 0;                     // the running total
int horizAverage = 0;
unsigned long currentdelayMillis;
unsigned long prevdelayMillis;
const int delayInterval = 5;

//ALIGNMENT VARIABLES
unsigned long currentAlignMillis = 0;
unsigned long prevAlignMillis = 0;
unsigned long AlignedInterval = 10000;

//LIE FLAT VARIABLES
//unsigned long currentLieFlatMillis = 0;
unsigned long prevLieFlatMillis = 0;
unsigned long lieFlatInterval = 20000;
boolean vertFlat = false;
boolean horizFlat = false;
boolean lieFlatTrigger = false;

int tolerance = 1;

void setup()
{
    Serial.begin(9600);
   
// INITIALIZE ALL WIND SENSOR READINGS TO 0:
  for (int thisReadingA = 0; thisReadingA < numReadings; thisReadingA++)
    vertReadings[thisReadingA] = 0;
  for (int thisReadingB = 0; thisReadingB < numReadings; thisReadingB++)
    horizReadings[thisReadingB] = 0;

// SET PIN MODES FOR LEDS    
    pinMode(redLEDpin, OUTPUT);
    pinMode(greenLEDpin, OUTPUT);
    pinMode(blueLEDpin, OUTPUT);
    digitalWrite(blueLEDpin, HIGH);
    digitalWrite(greenLEDpin, HIGH);
    digitalWrite(redLEDpin, LOW);
    delay(4000);
    digitalWrite(redLEDpin, HIGH);

// SET PIN MODES FOR MOTOR DRIVER    
    pinMode(AIN1, OUTPUT);
    pinMode(AIN2, OUTPUT);
    pinMode(BIN1, OUTPUT);
    pinMode(BIN2, OUTPUT);
    
    pinMode(APWM, OUTPUT);
    pinMode(BPWM, OUTPUT);
    pinMode(StandBy, OUTPUT);
    
    digitalWrite(AIN1, LOW);
    digitalWrite(AIN2, LOW);
    digitalWrite(BIN1, LOW);
    digitalWrite(BIN2, LOW);
    
    digitalWrite(APWM, LOW);
    digitalWrite(BPWM, LOW);
    digitalWrite(StandBy, LOW);
    
}
void loop(){
   
   CheckLight();   //CHECK WIND AND LIGHT CONDITIONS:
   CheckWind();
   
   if(lieFlatTrigger == true){
      LieFlat();                       //MOVE PANELS SO THEY ARE FLAT AND LEVEL FOR 
    //  Serial.print("  Lie Flat ");  //ETHER HIGH WINDS OR LOW LIGHT (NIGHT TIME).
   }
   else if((LightState == true) && (WindSpeed == true) && (lieFlatTrigger == false)){
      UpdatePosition();                       //ONLY WHEN ALL CONDITIONS ARE 
    //  Serial.print("   UpdatePosition  ");  //MET IS IT OK TO UPDATE POSITION. 
   }
   if(lieFlatTrigger == true){
     Serial.print(" lieFlatTrigger = true ");
   }
   else{
     Serial.print(" lieFlatTrigger = false ");
   }
      if(LightState == true){
     Serial.print(" LightState = true ");
   }
   else{
     Serial.print(" LightState = false ");
   }
      if(WindSpeed == true){
     Serial.println(" WindSpeed = true ");
   }
   else{
     Serial.println(" WindSpeed = false ");
   }
}


void LieFlat(){

  unsigned long currentLieFlatMillis = millis();
     CheckPots();
     
  if((vertFlat == true) && (horizFlat == true)){   // ONLY IF BOTH CONDITIONS ARE TRUE THEN
     digitalWrite(greenLEDpin, HIGH);             // THEN FADE RED LED OFF AND ON.
     digitalWrite(blueLEDpin, HIGH); 
     time = millis();
     value = 128+127*cos(2*PI/slowFlash*time);
     analogWrite(redLEDpin, value);
  }
  else if((vertFlat == false) && (horizFlat == false)){    // WHILE MOVING INTO FLAT AND LEVEL
     digitalWrite(redLEDpin, LOW);                        // POSITION TURN RED LED ON
     digitalWrite(greenLEDpin, HIGH);
     digitalWrite(blueLEDpin, HIGH);
  }

  if((vertFlat == false) || (horizFlat == false)){  
     digitalWrite(StandBy, HIGH);
     analogWrite(APWM, 255);
     analogWrite(BPWM, 255);
     
        if(vertAverage <= vertPotMax){
          digitalWrite(AIN1, HIGH);       //MOVE VERTICAL MOTOR ACTUATOR TO THE TOP
          digitalWrite(AIN2, LOW);
          
       }
       else{
          digitalWrite(AIN1, LOW);         
          digitalWrite(AIN2, LOW);
          digitalWrite(APWM, LOW);
          vertFlat = true;              // AND TURN OFF MOTOR ONCE AT VERTPOTMAX 
       }

                                         //MOVE HORIZONTAL MOTOR ACTUATOR TO THE CENTER      
       if(horizAverage <= horizPotMid - deadBand){
          digitalWrite(BIN1, HIGH);
          digitalWrite(BIN2, LOW);
          
       }
       else if(horizAverage >= horizPotMid + deadBand){ 
          digitalWrite(BIN1, LOW);
          digitalWrite(BIN2, HIGH);
          
       }
       else{
          digitalWrite(BIN1, LOW);
          digitalWrite(BIN2, LOW);
          digitalWrite(BPWM, LOW);
          horizFlat = true;   
       }
  }
  if((vertFlat == true) && (horizFlat == true)){
     digitalWrite(StandBy, LOW);                    //TURN OFF MOTOR DRIVER
        //  Serial.print("   Lying Flat Now ");
  }
                                          //WAIT FOR LIE FLAT INTERVAL THEN RESET FLAGS 
  if((unsigned long)currentLieFlatMillis - prevLieFlatMillis >= lieFlatInterval){
     lieFlatTrigger = false;
     vertFlat = false;
     horizFlat = false;
     Serial.print("   PING !!!!!!!!!!!!!          ");
     prevLieFlatMillis = currentLieFlatMillis;
  }
}
void UpdatePosition(){
   
   currentAlignMillis = millis();

  if((motorAstate == true) || (motorBstate == true)){   
     digitalWrite(redLEDpin, HIGH);                    //WHEN MOVING INTO ALIGNMENT TURN
     digitalWrite(blueLEDpin, LOW);                   // BLUE LED ON
     digitalWrite(greenLEDpin, HIGH);
  }
  else if((motorAstate == false) && (motorBstate == false)){
      digitalWrite(redLEDpin, HIGH);
      digitalWrite(blueLEDpin, HIGH);
      time = millis();                            //WHEN ALIGNED FADE GREEN LED ON AND OFF
      value = 128+127*cos(2*PI/slowFlash*time);
      analogWrite(greenLEDpin, value);
  }
  
  if(motorAstate == true){ 
// check if the diffirence is within the tolerance else change vertical angle  
     if(-1*tolerance > dvert || dvert > tolerance){
        digitalWrite(StandBy, HIGH);
        analogWrite(APWM, 200);
      
        if(avt > avb){                 //DRIVE MOTOR DOWN
           digitalWrite(AIN1, HIGH);
           digitalWrite(AIN2, LOW);
        }
        else if(avt < avb){            //DRIVE MOTOR UP
           digitalWrite(AIN1, LOW);
           digitalWrite(AIN2, HIGH);
        }
     }
     else{                   // IF THE DIFFERENCE IS WITHIN THE TOLERANCE TURN OFF MOTOR
        digitalWrite(AIN1, LOW);
        digitalWrite(AIN2, LOW);
        digitalWrite(APWM, LOW);
        motorAstate = false;
     }
  }

  if(motorBstate == true){  
// check if the diffirence is within the tolerance else change horizontal angle 
     if(-1*tolerance > dhoriz || dhoriz > tolerance){
        digitalWrite(StandBy, HIGH);
        analogWrite(BPWM, 200);
    
        if(avl > avr){                      //DRIVE MOTOR LEFT
           digitalWrite(BIN1, HIGH);
           digitalWrite(BIN2, LOW);
        }
        else if(avl < avr){                  //DRIVE MOTOR RIGHT
           digitalWrite(BIN1, LOW);
           digitalWrite(BIN2, HIGH);
        }
     }
     else{                    // IF THE DIFFERENCE IS WITHIN THE TOLERANCE TURN OFF MOTOR
        digitalWrite(BIN1, LOW);
        digitalWrite(BIN2, LOW);
        digitalWrite(BPWM, LOW);
        motorBstate = false;
     }
  }

  if((motorAstate == false) && (motorBstate == false)){ 
//   Serial.print("   Aligned  ");
     digitalWrite(StandBy, LOW);    //TURN OFF MOTOR DRIVER FOR THE stopInterval.
    
     if((unsigned long)currentAlignMillis - prevAlignMillis >= AlignedInterval){    
        motorAstate = true; //ONCE THE ALIGNED INTERVAL HAS FINISHED SET MOTOR STATES TO HIGH
        motorBstate = true;
        
        prevAlignMillis = currentAlignMillis;    //SET PREVMILLIS TO CURRENTMILLIS
     }
   }
}

void CheckLight(){
  
  lt = analogRead(LeftTopLDR);          // READ AND CALCULATE LIGHT SENSORS
  rt = analogRead(RightTopLDR); 
  lb = analogRead(LeftBottomLDR); 
  rb = analogRead(RightBottomLDR);
  
  avt = (lt + rt) / 2; // average value top
  avb = (lb + rb) / 2; // average value bottom
  avl = (lt + lb) / 2; // average value left
  avr = (rt + rb) / 2; // average value right
  dvert = avt - avb; // calculate the diffirence between the top and bottom
  dhoriz = avl - avr;// calculate the diffirence between the left and rigt
  
  int aveLight = (lt + rt + lb + rb) / 4;//GET THE AVERAGE LIGHT READING FROM THE LDR's
//Serial.print("  Average Light ");
//Serial.print(aveLight);
  
  if(aveLight >= minLight){        // IF THERE IS ENOUGH LIGHT
     LightState = true;
  }
  else{
     LightState = false;
     lieFlatTrigger = true;           // SETS THE CONDITION FOR THE LIE FLAT FUNCTION 
  }
}

void CheckWind(){
  
  int windSpeedVal = analogRead (WindSensor);      // READ THE WIND SENSOR
//Serial.print("  windSpeed ");
//Serial.println(windSpeed);
  
  if(windSpeedVal <= maxWind){                     // IF THE WIND IS NOT TO STRONG
     WindSpeed = true;
  }
  else{
     WindSpeed = false;
     lieFlatTrigger = true;             // SETS THE CONDITION FOR THE LIE FLAT FUNCTION 
  }
}

void CheckPots(){
    
  currentdelayMillis = millis();
                                                 //if delay interval has past...
  if((unsigned long)currentdelayMillis - prevdelayMillis >= delayInterval){ 
  //VERTICAL POTENTIOMETER  
      vertTotal= vertTotal - vertReadings[vertIndex];       // subtract the last reading:      
      vertReadings[vertIndex] = analogRead(vertPotPin);    // read from the sensor:
      vertTotal= vertTotal + vertReadings[vertIndex];     // add the reading to the total:     
      vertIndex = vertIndex + 1;           // advance to the next position in the array:            

          if (vertIndex >= numReadings){         // if we're at the end of the array...          
              vertIndex = 0;                    // ...wrap around to the beginning:                           
          }  
      vertAverage = vertTotal / numReadings;
      
  //HORIZONTAL POTENTIOMETER           
      horizTotal= horizTotal - horizReadings[horizIndex];       // subtract the last reading:      
      horizReadings[horizIndex] = analogRead(horizPotPin);     // read from the sensor:
      horizTotal= horizTotal + horizReadings[horizIndex];     // add the reading to the total:     
      horizIndex = horizIndex + 1;           // advance to the next position in the array:            

          if (horizIndex >= numReadings){         // if we're at the end of the array...          
              horizIndex = 0;                    // ...wrap around to the beginning:                           
          }   
      horizAverage = horizTotal / numReadings; 
 
            prevdelayMillis = currentdelayMillis;
   }
}

Please post your complete program in one piece - attach your .ino file if it exceeds the 9000 char limit. I am not going to take the risk of making an error joining the pieces together.

...R

Hi Robin, I'll attach the code when I get home. The way I have it laid out is in tabs, so each void() has it's own tab...(I found it quicker and easier to find stuff that way). So I'll piece it together in one tab, test it then attach post it.

Please find the code attached

Solar_Tracker.ino (12 KB)

Thanks. I suspect that the problem is that you are not saving prevLieFlatMillis at the right place. What is being used with that code is the value from the previous lie-down. I think the simple solution is to record prevLieFlatMillis whenever lieFlatTrigger is set to true.

There is no need to update prevLieFlatMillis in the LieFlat() function.

And if you think about it startLieFlatMillis would be a better name and might have identified the problem.

Hope this helps. I nearly missed it and I may be quite wrong.

...R
PS Full marks for a program that is easy to follow.

PS Full marks for a program that is easy to follow.

Thanks Robin, that means a lot coming from you.

You were right on the money. The prevLieFlatMillis was storing an outdated val by the time the LieFlat() function was called.

The way I fixed it was to implement this just before the interval ran.

    if(isLieFlatFirstLoop == true){          //RESET PREVIOUS LIE FLAT MILLIS 
        prevLieFlatMillis = startLieFlatMillis;
        isLieFlatFirstLoop = false;
     }

And then after the lieFlatInterval the isLieFlatFirstLoop is set to true again.

     if((unsigned long)startLieFlatMillis - prevLieFlatMillis >= lieFlatInterval){
        lieFlatTrigger = false;
        motorAstate = true; 
        motorBstate = true;
    //    Serial.print("   PING !!!!!!!!!!!!!          ");
        isLieFlatFirstLoop = true;
     }

Just a couple more things to build into it and will be good to go.
Thanks.

Can anyone please share the working version and diagram of this project.