Beekeepers Sun Tracking Solar Wax Melter Project - Arduino Uno

Thread Starter

rhulandjc2008

Joined Aug 25, 2012
7
Hi Folks,
History and Project Description

I am a Honeybee Beekeeper and I have started a winter project of creating a sun tracking solar wax melter, to melt beeswax from my bee hives. My current wax melter I have to manually re-position every half-hour to hour throughout the day to take advantage of the suns rays and heat to melt the wax.

I am looking at using a Arduino Uno to monitor and control several funtions:
1)LDR's & Horizontal Mounted Servo, to track and rotate the wax melter as the sun moves (most likely update in 15 minute intervals)
2)DHT22 Temperature/Humidity sensor with a small PWM cooling fan to (well try to anyway) maintain 140F-155F inside the melter.
3)LCD to Display Temperature Humidity and Fan Speed 0-100% (real time)
4)Save Temperature Humidity & Fan Speed data to a SD Shield in something like 5-15 minute intervals to review later(added just for kicks)

I have a little background in basic old school electronic circuits, but am limited in the scope of micro processing and coding.

I have written some code for:

1) LDR's & Horizontal mount servo... a little buggy and still working out trying to incorporate some sort of tolerance feature so not to constantly fight between the Left & Right LDR's, not having much luck with this...but I have been able to track a flash light. Also Looking to-re code this with the VarSpeedServo Library I came across for speed control
2) DHT22 & Fan circuit is built/coded out and seems to be working 100%
3) LCD Display is coded out and working but makes the Servo motion Jitter/Stutter...even more so when I have serial monitor enabled. Not sure how to reduce the Jitter/Stutter this feature is putting on the servo motion.
4) SD Shield haven't attempt incorporating this yet
5) Haven't looked into the 5-15 minute intervals i would like to add to certain areas...for now keeping it somewhat real time for testing/troubleshooting purposes.

Any suggestions to fix/streamline some of this would be greatly appreciated...again this is a winter project so I am not in a big hurry to get it up and running. I made a mock test platform to work on and plan to get the circuit working first on the bench...then build the full scale rotating enclosure.

Here is my code so far...please excuse the mess it is in...again my skills are poor (which I know is a understatement).
Code:
//**********************  Interrupt Service Routine - Soft E-Stop  **************************
boolean start = true;
volatile boolean e_stop = false;

//***********************  Liquid Crystal-I2C  ******************************************************
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x27                          // <<- Add your address here.
#define Rs_pin 0
#define Rw_pin 1
#define En_pin 2
#define BACKLIGHT_PIN 3
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

//***********************  Temp-Fan  ******************************************************
#include <DHT.h>                  // add DHT library
#define DHTPIN A0                 // what pin we're connected to
#define DHTTYPE DHT22             // DHT 22  (AM2302)
DHT dht(DHTPIN, DHTTYPE);         // set DHT condition
#define fan 10                    // fan Pin 10
//int humMin = 40;                  // min humindy set point
//int humMax = 100;                 // max humindy set point
int tempMin = 70;                 // min Temperature "degrees F" set point
int tempMax = 80;                 // max Temperature "Degrees F" set point
int fanSpeed;
int fanLCD;


//**********************  Servo/LDR Sun Tracking  ***********************************************
#include <Servo.h>
Servo servo1;               // create servo object to control a servo
int pos = 90;               // initial position
int LLDR = A1;              // LRD1 pin1
int RLDR = A2;              // LDR2 pin2
int tolerance = 3;          // <<- Set LDR Tolerance set point

//******************************************************************************************************************
//*************************  SETUP  *****************************************************************************
//******************************************************************************************************************
void setup(){
  Serial.begin(9600);                             // begin writing to the serial monitior
  attachInterrupt(0, e_stop_ISR ,RISING);         // add interrupt for soft e_stop feature

  lcd.begin (20,4);                                     // <<-- our LCD is a 20x4, change for your LCD if needed
  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);          // set backlight condition
  lcd.setBacklight(HIGH);                               // turn backlight on
  lcd.home ();                                          // go home on LCD
  lcd.print("Wax Melter v1.0");                         // print to LCD
  dht.begin();                  // initializes the inerface with the DHT22 Sensor
  pinMode(fan, OUTPUT);         // Set Fan as a OUTPUT Signal

  servo1.attach(9);             // attaches the servo on pin 9 to the servo object
  servo1.write(90);             // Move servo1 to 90 degree position
  pinMode(LLDR, INPUT);         // Set leftLDR as INPUT signal
  pinMode(RLDR, INPUT);         // Set rightLDR as INPUT signal
  delay(2000);                  // a 2 second delay while we position the solar wax melter
}

//***************************************************************************************************
//***********************   LOOP   *****************************************************************
//***************************************************************************************************
void loop(){
//*********************  Interrupt Service Routine - Soft E-Stop  *******************************
  if(start == true){
    if(e_stop == false){

//*********************  Temp-Fan  ***************************************************************
                                                       
    float h = dht.readHumidity();                           // read Humidity
    float t = dht.readTemperature();                        // read temperature as Celsius
    int f = (t * 1.8 + 32);                                 // convert to Farenheit
                             
    if( t < tempMin){                                       // if temperature is lower than minimum temperature
      fanSpeed = 0;                                         // fan = off
      digitalWrite(fan, LOW);                               // turn fan off
    }
    if((f > tempMin) && ( f <= tempMax)){                   // if temperatureis >= Min and <= Max setpoint
      fanSpeed = map(f, tempMin, tempMax, 32, 255);         // map fan speed
      fanLCD = map(f, tempMin, tempMax, 0, 100);            // speed of fan to display on LCD
      analogWrite(fan, fanSpeed);                           // spin the fan at the fanSpeed speed
    }
           
//*********************  Servo/LDR Sun Tracking  *******************************************************
   
    int pos = servo1.read();                  // read the value of Left LDR
    int leftLDR = analogRead(LLDR);           // read the value of Right LDR
    int rightLDR = analogRead(RLDR);          // read the value of Left LDR
     
    if((abs(leftLDR - rightLDR) < tolerance) || (abs(rightLDR - leftLDR) < tolerance)){}
    // do nothing if the diff. between values is within the tolerance limit
 
      else{                  
        if(leftLDR > rightLDR){         // if leftLDR value is greater than rightLDR value
          pos = --pos;                  // move servo in the negitive direction
          delay(100);                   // wait time to move servo
        }
        if(leftLDR < rightLDR){         // if leftLDR value is less than rightLDR value
          pos = ++pos;                  // move servo in the positive direction
          delay(100);                   // wait time to move servo
        }
      }
      if(pos > 180){          // reset to 170 if it goes higher
        pos = 170;            // move servo to 170 degrees
        servo1.write(pos);      // write the position to servo
        //delay(1000);            // wait time to move servo
      }
      if(pos < 0){            // reset to 10 degrees if it goes lower
        pos = 10;             // move servo to 10 degrees
        servo1.write(pos);      // write the position to servo
        //delay(1000);            // wait time to move servo
      }
         
      Serial.print("Humidity:       ");
      Serial.print(h);
      Serial.println("%\t");
      Serial.print("Temperature:    ");
      Serial.print(f);
      Serial.println("F");
      Serial.println();
      Serial.print("LeftLDR:           ");
      Serial.println(leftLDR);
      Serial.print("RightLDR Value:     ");
      Serial.println(rightLDR);
      Serial.println();
   
      lcd.setCursor(0,0);
      lcd.print("TEMPERATURE: ");
      lcd.print(f);                     // display the temperature
      lcd.print("F");
      lcd.setCursor(0,1);
      lcd.print("Humidity: ");
      lcd.print(h);                    // display the Humidity
      lcd.print("%");
      lcd.setCursor(0,2);
      lcd.print("FANS: ");
      lcd.print(fanLCD);               // display the fan speed
      lcd.print("%");        
    }
  }
  else{
    start = false;
  }
}

//*********************  Interrupt Service Routine - Soft E-Stop  ******************************************
void e_stop_ISR(void){
  detachInterrupt(0);
  e_stop = !e_stop;
}
Moderator edit: added code tags
 

Thread Starter

rhulandjc2008

Joined Aug 25, 2012
7
I have briefly looked a averaging formula, but haven't really looked into it...and no time wise...to take average readings would not be a issue i don't think, since eventually I would like to get most things on a 15 minute update if possible...I don't need to track sun every second/minute just periodically re-position to the sun.

Biggest issue I have right now is in the tolerance portion...servo seems only to correct/drive to the outer limit of tolerance so it is constantly trying to correct itself. I have tried several ways to try and drive it to the middle point or zero point of tolerance, with no luck.
 

Thread Starter

rhulandjc2008

Joined Aug 25, 2012
7
The servo itself is hunting ?
I wouldn't say hunting... I tried setting up a tolerance function to provide a "dead zone" so the servo would not constantly try and correct itself

"if((abs(leftLDR - rightLDR) < tolerance) || (abs(rightLDR - leftLDR) < tolerance)){}
// do nothing if the diff. between values is within the tolerance limit

But all this does it seems...is the Servo moves to the edge of the Tolerance/dead zone. I haven't figured a way to move the servo to ~the Zero point or center of the LDR's when using the tolerance function.
 

dannyf

Joined Sep 13, 2015
2,197
line followers can be built with a 555 timer + two LDRs. in this case, two 555 timers + 4 ldr should be sufficient for two dimensional tracking.
 

djsfantasi

Joined Apr 11, 2010
9,163
pos++ would be equiv to pos_step = 1

Is that correct ?
Where did pos_step come from? It's not defined in the TS sketch.

pos=++pos is a redundant construct. ++pos by itself increments the variable pos by 1. The statement in the sketch says increment pos, storing the result in pos and then assign the value in pos to pos.

What is the expected versus actual range of values do you expect from your LDRs? The range returned from an analog read is between 0-1023, in your case, since you are following the sun, most of your values are going to be clustered around some value. Hence, your tolerance may need adjusting.

I would run an experiment. Write a test sketch to display the two values to the console. Then, manually follow the sun and note the returned values. How different are they when you've manually pointed the device toward the sun? How much do they vary on this case? Also note how different the values are before you have to move it. These experimental values will tell you what tolerance you need, when the device should be moved, etc...

I'd code it so you only check and perform a move every n minutes, rather than constantly checking and moving and checking and moving.
 

Sensacell

Joined Jun 19, 2012
3,448
Good idea to drop the sample rate down, it will make debugging it easier too.

It's always smart to consider the "service rate" required to perform a given task, you can save CPU bandwidth for other tasks or interleave the process with other routines to avoid timing problems.

It's easy to just load a program full of library functions and expect it to work, but timing nuances can really kick your ass when using this strategy.
 

Thread Starter

rhulandjc2008

Joined Aug 25, 2012
7
Could it be the "tolerance" constant is a value low too compared to the value read from each LDR.

Are the LCD write & serial write functions having any impact on the servo ? Is there a way to synchronise the write functions to the LCD & serial port between servo pulse updates ?

Not knowing how the library routines are implemented I'm just throwing around a few thoughts.
No the Tolerance is not having a impact...I
Could it be the "tolerance" constant is a value low too compared to the value read from each LDR.

Are the LCD write & serial write functions having any impact on the servo ? Is there a way to synchronise the write functions to the LCD & serial port between servo pulse updates ?

Not knowing how the library routines are implemented I'm just throwing around a few thoughts.
No the Tolerance Value does not have a impact I have adjusted up and down with no effect...I que'd the LDR's and if I call for a tolerance of say 30 the servo slews UNTIL the LDR diff. values reaches the 30 threshold...not near 0. if I tighten it up and call for 15 it goes to 15...5 to 5. I can't get it to slew into the tolerance zone or near the Zero point of the LDR's...so the tolerance/dead zone can take effect "for a bit until the sun moves enough out of the dead zone for a correction". Instead the servo still constantly is adjusting itself as it slewed to the tolerance edge(30) and not center (0). it is basically doing the opposite of what I would like...which is to operate within the specified tolerance say 15 which in my mind would be +-15 from theoretical LDR zero which would give a tolerance range of 30. Instead it is operating at the outer edge of 15. Note these values are arbitrary numbers at this point.

Yes the LCD/serial write functions interrupt the slewing of the servo when I have them enabled...not sure how to get around this...for the SD Card write (which I haven't even started on)...was thinking maybe adding a interrupt function tied to a 555 timer so that say every 5 10 or 15 minutes it triggers the interrupt to send temp/humidity data to the card.

Not really sure how picky I need to be with this...after all the time scale on this will probably be on the order of 10-15 minute updates/adjustments. I'm chasing the sun not a fighter jet!!! Still fun to try anyway...
 

dannyf

Joined Sep 13, 2015
2,197
some suggestions:

1) put the two LDRs in serial (and maybe with a pot in the middle to adjust for slight unbalance between the two LDRs): with that setup, you need 1 adc to figure out which direction to turn.

2) consider some control algorithm for the position: you may want to consider historical positions, changes in positions, etc. it doesn't have to be full-blown PID but some form of it, at least PI, would be helpful.

3) consider lowering the gain of the control loop: slowing it down for example.

my code would look something like this:

Code:
  pos = adc2pos(readLDR()); //read LDR adc and convert it to target position
  servo.write(pos); //adjust servo position
  //do other things
  delay_min(LOOP_DLY); //waste some minutes
as the 10-bit adc is a little bit too much, the readLDR() function can return an 8-bit type:

Code:
uint8_t readLDR(void) {
  return analogRead(LDR_PIN) >> 2;
}
the adc2pos() is your control algorithm:
Code:
uint8_t adc2pos(uint8_t pos) {
  if (pos > POS_TARGET + POS_DELTA) return (pos - POS_STEP > POS_MIN)?(pos - POS_STEP):POS_MIN;
  if (pos < POS_TARGET - POS_DELTA) return (pos + POS_STEP < POS_MAX)?(pos + POS_STEP):POS_MAX;
  return pos;
}
what it does is to see if pos falls into a band (POS_DELTA) around the target (POS_TARGET, or 128/8bit or 512/10bit, essentially trying to assure that both LDSs have the same resistance). If pos is outside of the band, it increments or decrements pos by POS_STEP.

the goal is to isolate the various functions into individual blocks so you can tackle them one at a time, without producing a spaghetti of code.
 

dannyf

Joined Sep 13, 2015
2,197
to give you some hope, :)

I simulated the servo algorithm I put forth earlier. the LDR divider voltage comes in on A0 - in this case I force-feed it through a sine wave. the goal is to maintain that voltage at 2.5v - POS_TARGET = 128 in my case. rather than using a servo which I have no model for, I used a pwm output on D6 to simulate repositioning of that servo.

The green trace simulates the sun light and the red track simulates the re-balanced sun light on the tracker -> it should be around 2.5v if ithe code works perfectly.

and it did.
tracker_arduino.PNG
 

ErnieM

Joined Apr 24, 2011
8,377
Do you really need to do this the hard way? Why not take your manual set up, note how far you move it every half hour, then make a controller that moves it the same amount in the same time.
 

Thread Starter

rhulandjc2008

Joined Aug 25, 2012
7
some suggestions:

1) put the two LDRs in serial (and maybe with a pot in the middle to adjust for slight unbalance between the two LDRs): with that setup, you need 1 adc to figure out which direction to turn.

2) consider some control algorithm for the position: you may want to consider historical positions, changes in positions, etc. it doesn't have to be full-blown PID but some form of it, at least PI, would be helpful.

3) consider lowering the gain of the control loop: slowing it down for example.

my code would look something like this:

Code:
  pos = adc2pos(readLDR()); //read LDR adc and convert it to target position
  servo.write(pos); //adjust servo position
  //do other things
  delay_min(LOOP_DLY); //waste some minutes
as the 10-bit adc is a little bit too much, the readLDR() function can return an 8-bit type:

Code:
uint8_t readLDR(void) {
  return analogRead(LDR_PIN) >> 2;
}
the adc2pos() is your control algorithm:
Code:
uint8_t adc2pos(uint8_t pos) {
  if (pos > POS_TARGET + POS_DELTA) return (pos - POS_STEP > POS_MIN)?(pos - POS_STEP):POS_MIN;
  if (pos < POS_TARGET - POS_DELTA) return (pos + POS_STEP < POS_MAX)?(pos + POS_STEP):POS_MAX;
  return pos;
}
what it does is to see if pos falls into a band (POS_DELTA) around the target (POS_TARGET, or 128/8bit or 512/10bit, essentially trying to assure that both LDSs have the same resistance). If pos is outside of the band, it increments or decrements pos by POS_STEP.

the goal is to isolate the various functions into individual blocks so you can tackle them one at a time, without producing a spaghetti of code.
Thanks dannyf,
This looks like what I am trying to do with keeping the "melter" pointed a a light source, (flash-light for now...with goal to be the sun once built) and if light position exceeds a predetermined range send a correction adjustment to re-position the melter. I will take your Ideas and see what i can come up with.

As for the DHT22 Temp/Hum sensor...I would like to try and do a few things with it 1) update the LCD display I want to mount on the melter( was hoping for real time updates to control a PMW cooling fan to keep the temp above 140F (melting point) and below 155-160F(damages/changes wax properties). 2) Print to a SDcard shield every 10-15 or (X amount of) minutes to view/chart later...this Item 2 is more for fun then operational importance. I was thinking along the lines of a 555 time/interrupt sequence as I have read Arduino internal timers limit to roughly 8 minutes and having any delays it the loop would put a hold on anything else. Any thoughts with this approach? Worst case is I time the system to under 8 minutes.

Again thanks for the advice.
 

Thread Starter

rhulandjc2008

Joined Aug 25, 2012
7
Do you really need to do this the hard way? Why not take your manual set up, note how far you move it every half hour, then make a controller that moves it the same amount in the same time.
Your right I could do it simpler with a simple controller circuit as you mentioned...but what fun is that... go big or go home!! This is a winter project to give me something to do to pass the boring Ohio winter. Plus it is a learning experience that would come in useful for few other projects i would like to build.
 

dannyf

Joined Sep 13, 2015
2,197
generally, it is fairly easy to add sensors / display. or fan control for that matter. complication could come into play since this is an outdoor winter project. Whether the lcds / batteries work that well is an unknown.

the key is to keep the code high level and modular so it is easy to debug and integrate into a final project that works the first time.

as to the timer, its capabilities can be extended easily, with a user isr handler, like this:

Code:
//in the timer isr
ISR(TMRx_vect) {
  static uint8/16/32_t cnt=ISR_CNT; //isr invocation counter
  cnt-=1;  //update cnt
  if (cnt == 0) { //ISR_CNT has been exhausted
    cnt = ISR_CNT; //reset cnt
    myISR(); //user isr
}
So you can easily specify the number of isr invocations you need before something is done, via myISR().

With a 16bit type, you can extend it 64K times. longer with a 32_bit type.
 

Thread Starter

rhulandjc2008

Joined Aug 25, 2012
7
complication could come into play since this is an outdoor winter project. Whether the lcds / batteries work that well is an unknown.
This wont be used over winter sun angle and outside Temps are not strong/warm enough to heat the melter up to 140F...during these winter months. Using the time to design test and build...with the goal to be done by spring...we had a crappy fall here in Northern Ohio and I have a bunch of wax (5 gallon bucket worth from this past fall harvest (200lbs of honey collected) that I need to melt down and doing over the stove is a messy business...not to mention the slight fire hazard being wax with a open stove flame...best to do safely out side and NOT burn the house down!!!

Again thanks for your time and pointers am on Vacation until the New Year so I should have time to work on it...as long as my bottle of aspirin holds out for the headaches that are sure to come...LOL!!! Its a bit of learning process for me. Will keep you posted!!!
 

PhilTilson

Joined Nov 29, 2009
131
Your right I could do it simpler with a simple controller circuit as you mentioned...but what fun is that... go big or go home!! This is a winter project to give me something to do to pass the boring Ohio winter. Plus it is a learning experience that would come in useful for few other projects i would like to build.
I think ErnieM has a good point! Yes, it's fun to have something to do during the boring winter months but to over-complicate a system just for the hell of it seems a bit perverse!

How about a middle path: the sun only moves from east to west, so you are only going to move one way during the day. So, having 'balanced' your LDRs, all you need do is to fire off a process every 15 minutes that runs the motor to turn the unit until the LDRs are giving an equal output, then stop. Wait another 15 minutes then repeat. At the end of the day, run the motor in the opposite direction to a limit stop from which you can start again the next morning!

Phil
 
Top