ARDUINO-PROGRAMMCODE Version 8.9

/*

  Sonnenstandberechnung und Solartracking mit DS1307 RTC Zeit

  Sonnenstandsberechnung geht konform mit:

  https://lexikon.astronomie.info/zeitgleichung/

  - Mit LCD -Ausgabe -

  - mit Spannungswert PV-Modul -

  - Neu: mit Hall-Winkelsensor statt Drehpotentiometer -

  Positionsangaben für Ort: Rönkhausen

  Version 8.9, M. Schulte, 28. Mai 2019

  Änderung: Variable Grenzwert für Rücklauf eingefügt

  Änderung: Neuberechnung der Servoposition

  Änderung: Steppermotor vor und zurück von 25 auf 32 steps pro loop

  Änderung: Azimutpositionen neu kalibriert

*/

 

#include <Servo.h>

#include <Wire.h>

#include <DS3231.h>

#include <LiquidCrystal_I2C.h>

#include <Adafruit_MotorShield.h>

#include "utility/Adafruit_MS_PWMServoDriver.h"

 

Adafruit_MotorShield AFMS = Adafruit_MotorShield();

Adafruit_StepperMotor *stepper = AFMS.getStepper(200, 1);            // Steppermotor für Azimut

LiquidCrystal_I2C lcd(0x27, 20, 4);

RTCDateTime dt;

DS3231 clock;

Servo servo;                                                         // Servomotor für Elevation

 

double pi = 3.1415926;

double kwert = pi / 180;

double latitude = 51.222191;

double longitude = 7.954169;

double Elevation;

double elevation;

double Aufgang;

double Untergang;

 

 

void setup() {

  pinMode(11, OUTPUT);                                               // für grüne LED

  pinMode(12, OUTPUT);                                               // für rote LED

  AFMS.begin();

  stepper->setSpeed(10);

  lcd.init();                                                        // initialisiere 20x4 LCD

  lcd.backlight();

  lcd.clear();

  servo.attach(9);                                                   // Servo Signal von Pin 9

  servo.write(62);

  clock.begin();

  //clock.setDateTime(2019, 05, 18, 10, 31, 00);                     // Hier Datum und aktuelle Uhrzeit (MEZ) setzen

}

 

 

// Beginn der Schleife

 

void loop() {

 

  dt = clock.getDateTime();                                          // Zeitwert holen von RTC Time Echtzeituhr

 

  String hour = clock.dateFormat("H", dt);

  String minute = clock.dateFormat("i", dt);

  String year = clock.dateFormat("z", dt);

  String second = clock.dateFormat("s", dt);

 

  int yearday = year.toInt() + 1;

  int Jahr = (dt.year);

  int Monat = (dt.month);

  int Tag = (dt.day);

  double Stunde = hour.toInt();

  double Minute = minute.toInt();

  double Sekunde = second.toInt();

 

  double Deklination;

  double Azimut;

  double Zeitgleichung;

  double Zeitdifferenz;

  double AufgangOrtszeit;

  double UntergangOrtszeit;

  double Tageslaenge;

  double Refraktion;

  double MEZ;                                                                              // Mitteleuropäische Zeit

  double MOZ;                                                                              // Mittlere Ortszeit

  double WOZ;                                                                              // Wahre Ortszeit

  double ZeitSeitMittag;

  double B = latitude * kwert;                                                             // geogr. Breite in Radians

  double R = 0.00;                                                                         // Refraktion Anfangswert

  double h = -(50.0 / 60.0) * kwert;                                                       // Höhe des Sonnenmittelpunkts bei Aufgang: Radius + Refraktion

  int Grenzwert;

  int Zeitzone = 1;                                                                        // Zeitzone Greenwich + 1 (z.B. Berlin)

  float P = 1013.25;                                                                       // Luftdruck der Standard-Atmosphäre in hPa

  float T = 15.0;                                                                          // Temperatur der Standard-Atmosphäre in °C

  int zeitschritt = 1000;                                                                  // Zeitschritt loop = 1 sec

  int sensorValue;                                                                         // Wert Hall-Sensor

  int azimutPoti;                                                                          // Wert Hall-Sensor (Ist-Wert) Azimut

 

  MEZ = Stunde + Minute / 60 + Sekunde / 3600;

 

  Zeitgleichung = BerechneZeitgleichung (yearday);                                         // Aufruf der Funktion: BerechneZeitgleichung

  Deklination = BerechneDeklination (yearday);                                             // Aufruf der Funktion: BerechneDeklination

  Zeitdifferenz = BerechneZeitdifferenz (h, latitude, kwert, Deklination, pi);             // Aufruf der Funktion: BerechneZeitdifferenz

  ZeitSeitMittag = MEZ + longitude / 15.0 - Zeitzone - 12 + Zeitgleichung;                 // Berechne ZeitSeitMittag

  Azimut = SunAngles(Deklination, B, ZeitSeitMittag, Elevation);                           // Aufruf der Funktion: SunAngles

  Refraktion = BerechneRwert(Elevation, kwert, P, T);                                      // Aufruf der Funktion: BerechneRwert

  Elevation  = (Elevation + Refraktion) / kwert;                                           // Höhe mit Refraktionskorrektur in Grad

  Tageslaenge = BerechneTageslaenge (Zeitdifferenz, Zeitgleichung, longitude, Zeitzone, Aufgang, Untergang);   // Aufruf der Funktion: BerechneTageslaenge

 

  MOZ = MEZ - (-longitude / 15) - 1;                                                       // Berechne Mittlere Ortszeit aus MEZ

  WOZ = MOZ + Zeitgleichung;                                                               // Berechne Wahre Ortszeit

 

  int WOZdez = (int)WOZ;

  int WOZmin = (WOZ - WOZdez) * 60;

  int WOZsec = (WOZ - WOZdez) * 60;

 

  int MOZdez = (int)MOZ;

  int MOZmin = (MOZ - MOZdez) * 60;

  int MOZsec = (MOZ - MOZdez) * 60;

 

  int StdL = (int)Tageslaenge;

  int MinL = (int)((Tageslaenge - StdL) * 60);

 

  int StdA = (int)Aufgang;

  int MinA = (int)((Aufgang - StdA) * 60);

 

  int StdU = (int)Untergang;

  int MinU = (int)((Untergang - StdU) * 60);

 

  if (StdL >= 7 && StdL < 9) {                                                             // Grenzwerte für Rücklaufposition

    Grenzwert = 135;                                                                       // abhängig von der Tageslänge (nur Stunde)

  };

  if (StdL >= 9 && StdL < 10) {

    Grenzwert = 120;

  };

  if (StdL >= 10 && StdL < 11) {

    Grenzwert = 105;

  };

  if (StdL >= 11 && StdL < 13) {

    Grenzwert = 90;

  };

  if (StdL >= 13 && StdL < 14) {

    Grenzwert = 75;

  };

  if (StdL >= 14 && StdL < 15) {

    Grenzwert = 60;

  };

  if (StdL >= 15 && StdL < 17) {

    Grenzwert = 45;

  };

 

 

  // Servomotor für Elevation

 

  elevation = map(Elevation, 90, 0, 105, 3);                                        // Kalibrierung der Servoposition (elevation)

  servo.write(elevation);                                                           // Schreiben der Servoposition (Nachführung)

 

 

  // Steppermotor für Azimutposition

 

  sensorValue = analogRead(A0);                                                       // Auslesen Hall-Sensorwert an Pin A0

 

  if (sensorValue >= 125 && sensorValue < 506)  {                                     // Skalierung der Hall-Sensorwerte in Gradwerte 45-180 Grad

    azimutPoti = map(sensorValue, 125, 506, 45, 180);

  }

  if (sensorValue >= 506 && sensorValue <= 886)  {                                    // Skalierung der Hall-Sensorwerte in Gradwerte 180-315 Grad

    azimutPoti = map(sensorValue, 506, 886, 180, 315);

  }

 

  if (Elevation > 0) {                                                                // Solange Elevation > 0 --> Sonne steht über dem Horizont

 

    if (azimutPoti < (int)Azimut) {                                                   // Azimut-Nachführung: Wenn Hall-Sensorwert kleiner als Azimut, dann:

      digitalWrite(11, HIGH);                                                         // LED grün an

      digitalWrite(12, LOW);                                                          // LED rot aus

      stepper->step(32, FORWARD, MICROSTEP);                                          // Steppermotor dreht 32 steps vorwärts (entspricht +1 Grad des Drehkranzes)

    }

    if (azimutPoti > (int)Azimut) {                                                   // Azimut-Nachführung: Wenn Hall-Sensorwert grösser als Azimut, dann:

      digitalWrite(12, HIGH);                                                         // LED rot an

      digitalWrite(11, LOW);                                                          // LED grün aus

      stepper->step(32, BACKWARD, MICROSTEP);                                         // Steppermotor dreht 32 steps rückwärts (entspricht -1 Grad des Drehkranzes)

    }

  }

  else  {                                                                             // Sonne ist untergegangen --> Rückführung auf Anfangsposition

    if (Elevation < 0 && (azimutPoti - 1) >= Grenzwert)  {                            // Azimut-Rückführung: Wenn Elevation < -1 (Sonne ist untergegangen), dann:

      zeitschritt = 0;                                                                // temporär: ohne Pause, Zeitschritt ausgesetzt

      digitalWrite(12, HIGH);                                                         // LED rot an: Stepper dreht rückwärts

      digitalWrite(11, LOW);                                                          // LED grün aus

      stepper->step(100, BACKWARD, DOUBLE);                                           // Schneller Rücklauf mit 100 steps solange Hall-Sensorwert >= Grenzwert

    }

  }

 

 

  // Deklarationen und Typumwandlungen für LCD-Anzeige

 

  const char* windrichtung[16] = {"N  ", "NNO", "NO ", "ONO", "O  ", "OSO", "SO ", "SSO", "S  ", "SSW", "SW ", "WSW", "W  ", "WNW", "NW ", "NNW"};

 

  int i = Windrichtung(azimutPoti);                                                   // Aufruf Funktion: Windrichtung

 

  String wr = windrichtung[i];

  int analogPin = 2;

  float pvmod;

  float volt;

  byte stellen = 2;

  pvmod = analogRead(analogPin);                                                      // Abfrage Spannung Solar Panel

  volt = pvmod / 1023 * 4.75;                                                         // Umwandlung Analogwert in Spannungswert (Volt)

 

  int Std = (int)Tageslaenge;

  int Min = (int)((Tageslaenge - Std) * 60);

 

 

  // Ausgabe der Werte auf LCD-Display

 

  lcd.setCursor(0, 0);

  lcd.print(clock.dateFormat("d.M.Y H:i:s", dt));

 

  lcd.setCursor(0, 1);

  lcd.print("AzS");

  lcd.setCursor(4, 1);

  lcd.print(Azimut);

  lcd.write(0xDF);

  lcd.print(" ");

 

  lcd.setCursor(12, 1);

  lcd.print("WZ ");

  if (WOZdez < 10) lcd.print("0");

  lcd.print(WOZdez);

  lcd.print(":");

  if (WOZmin < 10) lcd.print("0");

  lcd.print(WOZmin);

 

  lcd.setCursor(0, 2);

  lcd.print("AzI ");

  lcd.print(azimutPoti);

  lcd.write(0xDF);

  lcd.print(wr);

  lcd.print(" ");

 

  lcd.setCursor(12, 2);

  lcd.print("TL ");

  if (Std < 10) lcd.print("0");

  lcd.print(Std);

  lcd.print(":");

  if (Min < 10) lcd.print("0");

  lcd.print(Min);

 

  lcd.setCursor(0, 3);

  lcd.print("Elv");

  lcd.setCursor(12, 3);

  lcd.print("PV");

  lcd.setCursor(3, 3);

  lcd.print("         ");

  lcd.setCursor(4, 3);

  lcd.print(Elevation, 2);

  lcd.write(0xDF);

  lcd.setCursor(15, 3);

  lcd.print(volt, 1);

  lcd.print(" V");

 

  delay(zeitschritt);  // Programmpause

}

 

 

// Funktion: Windrichtung

 

int Windrichtung (float azimutPoti) {

 

  int i;

 

  if (azimutPoti >= 0 && azimutPoti < 11.25)  {

    i = 0;

  }

  if (azimutPoti >= 11.25 && azimutPoti < 33.75)  {

    i = 1;

  }

  if (azimutPoti >= 33.75 && azimutPoti < 56.25)  {

    i = 2;

  }

  if (azimutPoti >= 56.25 && azimutPoti < 78.75)  {

    i = 3;

  }

  if (azimutPoti >= 78.75 && azimutPoti < 101.25)  {

    i = 4;

  }

  if (azimutPoti >= 101.25 && azimutPoti < 123.75)  {

    i = 5;

  }

  if (azimutPoti >= 123.75 && azimutPoti < 146.25)  {

    i = 6;

  }

  if (azimutPoti >= 146.25 && azimutPoti < 168.75)  {

    i = 7;

  }

  if (azimutPoti >= 168.75 && azimutPoti < 191.25)  {

    i = 8;

  }

  if (azimutPoti >= 191.25 && azimutPoti < 213.75)  {

    i = 9;

  }

  if (azimutPoti >= 213.75 && azimutPoti < 236.25)  {

    i = 10;

  }

  if (azimutPoti >= 236.25 && azimutPoti < 258.75)  {

    i = 11;

  }

  if (azimutPoti >= 258.75 && azimutPoti < 281.25)  {

    i = 12;

  }

  if (azimutPoti >= 281.25 && azimutPoti < 303.75)  {

    i = 13;

  }

  if (azimutPoti >= 303.75 && azimutPoti < 326.25)  {

    i = 14;

  }

  if (azimutPoti >= 326.25 && azimutPoti < 348.75)  {

    i = 15;

  }

  return i;

}

 

 

// Funktion: SunAngles für die Berechnung von Azimut und Elevation

 

double SunAngles (double Deklination, double B, double ZeitSeitMittag, double & Elevation) {

  double DK = Deklination;

  double cosdec = cos(DK);

  double sindec = sin(DK);

  double lha = ZeitSeitMittag * (1.0027379 - 1 / 365.25) * 15 * kwert;

  double coslha = cos(lha);

  double sinlha = sin(lha);

  double coslat = cos(B);

  double sinlat = sin(B);

  double N = -cosdec * sinlha;

  double D = sindec * coslat - cosdec * coslha * sinlat;

  Elevation = asin(sindec * sinlat + cosdec * coslha * coslat);                     // Höhe des Sonnenmittelpunktes

  double Azimut = atan2(N, D);

  if (Azimut < 0) {

    Azimut += 2 * pi;

  }

  Azimut = Azimut / kwert;

  return Azimut;

}

 

 

// Funktion: BerechneZeitgleichung

 

double BerechneZeitgleichung (int yearday) {

  double Zeitgleichung = -0.170869921174742 * sin(0.0336997028793971 * yearday + 0.465419984181394) - 0.129890681040717 * sin(0.0178674832556871 * yearday - 0.167936777524864);

  return Zeitgleichung;

}

 

 

// Funktion: BerechneDeklination

 

double BerechneDeklination(int yearday) {

  double Deklination = 0.409526325277017 * sin(0.0169060504029192 * (yearday - 80.0856919827619));

  return Deklination;

}

 

 

// Funktion: BerechneZeitdifferenz

 

double BerechneZeitdifferenz(double h, double latitude, double kwert, double Deklination, double pi) {

  double Zeitdifferenz;

  Zeitdifferenz = 12 * acos((sin(h) - sin(latitude * kwert) * sin(Deklination)) / (cos(latitude * kwert) * cos(Deklination))) / pi;

  return Zeitdifferenz;

}

 

 

// Funktion: BerechneRwert

// Näherungslösung für die Refraktion für ein Objekt bei geringer Höhe über mathematischem Horizont

// Refraktion beträgt bei Sonnenaufgang 34 Bogenminuten = 0.56667°

// Falls die Höhe der Sonne nicht genauer als auf 0.5° gewünscht ist, kann diese Funktion ignoriert werden

 

double BerechneRwert (double Elevation, double kwert, float P, float T)  {

  double R;

  if (Elevation >= 15 * kwert) {

    R = 0.00452 * kwert * P / tan(Elevation) / (273 + T);                                    // über 15° - einfachere Formel

  }

  else if (Elevation > -1 * kwert) {

    R = kwert * P * (0.1594 + 0.0196 * Elevation + 0.00002 * (Elevation * Elevation)) / ((273 + T) * (1 + 0.505 * Elevation + 0.0845 * (Elevation * Elevation)));

  }

  return R;

}

 

 

// Funktion: BerechneTageslaenge

 

double BerechneTageslaenge (double Zeitdifferenz, double Zeitgleichung, double longitude, int Zeitzone, double & Aufgang, double & Untergang)  {

  double AufgangOrtszeit = 12 - Zeitdifferenz - Zeitgleichung;

  double UntergangOrtszeit = 12 + Zeitdifferenz - Zeitgleichung;

  Aufgang = AufgangOrtszeit - longitude / 15 + Zeitzone;

  Untergang = UntergangOrtszeit - longitude / 15 + Zeitzone;

  double Tageslaenge = Untergang - Aufgang;

  return Tageslaenge;

}