Hello,
I am trying to build an heliostat using a mirror and 2 stepper motors in a Pan&tilt configuration.
Basically, the intention is to keep the mirror in a calculated position to reflect the sunlight always to the same spot throughout the day.
I'll use an Arduino UNO R3 board, a DS3231 real time clock, and 2 stepper motors 28BYJ-48 with 2 driver ULN2003.
For power input, I'll use 5V from the MB102 breadboard since I read that it's not good to power the steppers directly from the UNO board.
My code compiles and the calculations to determine angle instructions seem to work well. (I verified all variables calculated via the serial monitor)
I have not tested yet the steppers instructions as I don't have in hand the second stepper and driver yet, and I would also like to be sure of my code before trying it out in order not to damage anything...
I'm a newbie and it's my first time trying to build a code, so I'm pretty sure there is a lot to optimize, and probably a lot to correct as well. I did my best with the knowledge I could...
I'd very much appreciate your input to tell me whether this code can work and if it could be improved somehow.
I'm not looking for something perfect, but at least to have something viable.
I'm also interested if you have any advice on the hardware that I intend to use.
A few more specific questions I have:
- I added some enable/disable outputs function on the stepper, with in mind the feeling that it was useless to keep current going if the motor is not moving anyway. Does it make sense ?
- Based on another code I took example from, I also included a function to check whether the previous movement is over before starting next one. Not sure if that's necessary actually since the code should not continue to next instruction unless previous one if finished, right ?
- Are the stepper speeds I'm considering ok ? With the Accelstepper library, normally the speeds are in steps per seconds, so should be ok, right ?
- About the 28BYJ-48 steppers, I see in the datasheet 2048 full steps, but I also saw as per calculation that we actually have 2038 steps precisely. Any idea why do we have this discrepancy ?
Thanks for your support !
#include <AccelStepper.h>
#include <MultiStepper.h>
#include <RTClib.h>
#include <Wire.h>
RTC_DS3231 rtc;
const float Latitude = 43.32138, Longitude = 5.44302; //Position of the heliostat (random in this example).
float LatRad, LongRad;
float TempsUTC;
const int Angleface = -80, Anglefocal = 15;
float AngFaceRad, AngFocalRad;
int Daynumb;
float Declinaison, DecliRad, B, EquTemps, AngHoraire, HauteurSol, AzimutSol;
float AngleIncidence, InclinaisonMiroir, cosphiae, PhiAE ;
const float stepresolution = 360 / 2038 ; // Resolution of the 2 stepper motors 28BYJ-48.
float DegreeAz_order ;
float DegreeH_order ;
float StepAz_order = DegreeAz_order / stepresolution ;
float StepH_order = DegreeH_order / stepresolution ;
bool move_finishedAz ; // To check that previous movement is ended prior starting next one.
bool move_finishedH ; // To check that previous movement is ended prior starting next one.
//For mirror Azimut motor:
#define motorAzPin1 8 // IN1 sur le pilote ULN2003
#define motorAzPin2 9 // IN2 sur le pilote ULN2003
#define motorAzPin3 10 // IN3 sur le pilote ULN2003
#define motorAzPin4 11 // IN4 sur le pilote ULN2003
#define MotorAzInterfaceType 4
AccelStepper stepperAz = AccelStepper ( MotorAzInterfaceType, motorAzPin1, motorAzPin3, motorAzPin2, motorAzPin4 ) ;
//For mirror Height motor:
#define motorHPin1 4 // IN1 sur le pilote ULN2003
#define motorHPin2 5 // IN2 sur le pilote ULN2003
#define motorHPin3 6 // IN3 sur le pilote ULN2003
#define motorHPin4 7 // IN4 sur le pilote ULN2003
#define MotorHInterfaceType 4
AccelStepper stepperH = AccelStepper ( MotorHInterfaceType, motorHPin1, motorHPin3, motorHPin2, motorHPin4 ) ;
void setup()
{
Serial.begin(9600);
LatRad = radians(Latitude);
LongRad = radians(Longitude);
AngFaceRad = radians(Angleface);
AngFocalRad = radians(Anglefocal);
rtc.begin();
stepperAz.setCurrentPosition(0); //Upon start, mirror must be facing South.
stepperAz.setMaxSpeed ( 500 ) ;
stepperAz.setAcceleration(200);
move_finishedAz = 1 ;
stepperAz.disableOutputs(); //Turn power off on the motor by default.
stepperH.setCurrentPosition(0); //Upon start, mirror must be vertical.
stepperH.setMaxSpeed ( 300 ) ;
stepperH.setAcceleration(100);
move_finishedH = 1 ;
stepperH.disableOutputs(); //Turn power off on the motor by default.
}
void loop()
{
DateTime now = rtc.now();
while (now.hour() >= 8 && now.hour() <= 20 ) {
//Define daily time range of operation. From 08h00 till 20h00 (local time) in this case.
Daynumb = (now.unixtime() % 31556926) / 86400;
TempsUTC = -1 + float(now.hour()) + float(now.minute()) / 60 + float(now.second()) / 3600 ;
//Determine day and time to be used in astronomical calculations. Including time correction for UTC (-1h in this case).
//Start of ASTRONOMICAL CALCULATIONS ****************************************************************************************************
Declinaison = (180 / PI) * asin(sin(PI * 23.44 / 180) * sin((2 * PI / 365.25) * (Daynumb - 81)));
DecliRad = radians(Declinaison);
//Declinaison of the Sun based on day of the year since 01st of January.
B = 2 * PI * (Daynumb - 81) / 365.25;
EquTemps = 7.53 * cos(B) + 1.5 * sin(B) - 9.87 * sin(2 * B);
AngHoraire = ((TempsUTC - 12) + (Longitude / 15) - EquTemps / 60) * PI / 12;
//Time angle of the Sun based on Longitude and exact time.
HauteurSol = asin(sin(LatRad) * sin(DecliRad) + cos(LatRad) * cos(DecliRad) * cos(AngHoraire));
//Height of the sun versus horizon, in radians.
AzimutSol = asin(cos(DecliRad) * sin(AngHoraire) / cos(HauteurSol));
//Sun's Azimut from South in radians. Counted negative to East.
AngleIncidence = 0.5 * acos(sin(AngFocalRad) * sin(HauteurSol)
+ cos(AngFocalRad) * sin(AngFaceRad) * cos(HauteurSol) * sin(AzimutSol)
+ cos(AngFocalRad) * cos(AngFaceRad) * cos(HauteurSol) * cos(AzimutSol));
DegreeH_order = degrees(asin((sin(AngFocalRad) + sin(HauteurSol)) / (2 * cos(AngleIncidence))));
// Mirror tilt in degrees. Vertical being 0degrees.
cosphiae = (cos(HauteurSol) * cos(AzimutSol) + cos(AngFocalRad) * cos(AngFaceRad)) / (2 * cos(AngleIncidence) * cos(InclinaisonMiroir));
if (cosphiae >= 0) {
PhiAE = asin((cos(HauteurSol) * sin(AzimutSol) + cos(AngFocalRad) * sin(AngFaceRad)) / (2 * cos(AngleIncidence) * cos(InclinaisonMiroir)));
}
else {
PhiAE = PI - asin((cos(HauteurSol) * sin(AzimutSol) + cos(AngFocalRad) * sin(AngFaceRad)) / (2 * cos(AngleIncidence) * cos(InclinaisonMiroir)));
}
DegreeAz_order = degrees(PhiAE);
//Mirror Azimut in degrees. South being 0degrees, East negative, West positive.
//End of ASTRONOMICAL CALCULATIONS****************************************************************************************************
//Instructions for mirror's Azimutal and Height motors.
stepperAz.moveTo(StepAz_order);
if (move_finishedAz = 1 && (stepperAz.distanceToGo() != 0)) { //Check that motor not currently moving, and order is different then current position.
move_finishedAz = 0 ;
stepperAz.enableOutputs();
stepperAz.run();
}
if (stepperAz.distanceToGo() == 0) {
move_finishedAz = 1 ;
stepperAz.disableOutputs();
}
stepperH.moveTo(StepH_order);
if (move_finishedH = 1 && (stepperH.distanceToGo() != 0)) {
move_finishedH = 0 ;
stepperH.enableOutputs();
stepperH.run();
}
if (stepperH.distanceToGo() == 0) {
move_finishedH = 1 ;
stepperH.disableOutputs();
}
delay(10000); //Frequency of position adjustements.
}
}