Präzise Solar-Nachführung mit Servo-Plattform für maximalen Energieertrag
Komponenten und Zubehör
| Fotowiderstände (LDR, 10–20 kΩ dunkel) | × | 4 |
| RGB-Neopixel-LEDs (4er-Pack, z. B. Adafruit Flora) | × | 1 |
| Taster 12 mm (Normalerweise offen) | × | 2 |
| Temperatursensor (z. B. TMP36 oder LM35) | × | 1 |
| Arduino UNO | × | 1 |
| 180°-Servos (z. B. Futaba S3003 oder RobotGeek) | × | 2 |
| Widerstände 10 kΩ | × | 6 |
| Widerstand 220 Ω (für NeoPixel/LED) | × | 1 |
| Kondensator 100 µF (Entstörung Servos) | × | 2 |
| Solarmodul (ca. 80×80 mm) | × | 1 |
Benötigtes Werkzeug & Material
- Handwerkzeug, kleine Schrauben/Muttern für Halterung
- Fester Sockel für den „Dreh“-Servo (z. B. Holzklotz)
- Biegsames Metall / Alu-Streifen (ca. 3 mm dick, <2 cm breit) für „L“-Winkel
- Dünnes Stahlband (Lüftungsband, vorgebohrt) für Halterung Solarplattform am „Kipp“-Servo
Apps & Entwicklungsumgebung
- Arduino IDE
- Microsoft Excel (Dokumentation, Notizen)
Projektbeschreibung
Eine weitere Implementierung einer zweiachsigen Solarnachführung mit zwei 180°-Servos und vier Fotowiderständen (LDRs). Die Plattform richtet das kleine Solarmodul kontinuierlich auf die hellste Lichtquelle aus.
Die Motoren führen kleine, stetige Bewegungen aus, um dem Lichtmaximum zu folgen. Eine RGB-LED signalisiert den Betriebszustand (aktiv/inaktiv). Zwei Taster ermöglichen manuelles Ein-/Ausschalten der Nachführung.
Zusätzlich gibt es eine serielle Kommando-Schnittstelle (Numeric Codes), um Servopositionen gezielt anzufahren. Die Bewegungsroutinen sind mit Sanftanlauf/-stopp implementiert, um mechanische Erschütterungen zu vermeiden.
Arduino-Code (Q2 Version 2.0, C/C++)
Der Sketch ist modular aufgebaut: Initialisierung, Hauptschleife, Serielle Kommandos, Glättungsroutinen, Sensor-Mittelwertbildung.
// Scott Mangiacotti, Tucson AZ, May 2018 – Q2 v2.0
#include <Servo.h>
const int GIVE_BACK_TIME = 125; // Loop-Pause ms
// Pins
const int PIN_BTN_RED = 4; // Aus-Schalter
const int PIN_BTN_GREEN = 2; // Ein-Schalter
const int PIN_LDR_TL = A3; // Links oben
const int PIN_LDR_TR = A2; // Rechts oben
const int PIN_LDR_BL = A1; // Links unten
const int PIN_LDR_BR = A0; // Rechts unten
const int PIN_LED_R = 9; // NeoPixel R (PWM)
const int PIN_LED_G = 11; // NeoPixel G (PWM)
const int PIN_LED_B = 10; // NeoPixel B (PWM)
const int PIN_SERVO_TWIST = 5; // Basis-Drehung
const int PIN_SERVO_TILT = 6; // Neigung
// Globale Zustände
bool running = false;
bool trackEnabled = false;
bool verbose = false;
Servo servoTwist, servoTilt;
int cmdTwistPos, cmdTiltPos;
bool movingTwist = false, movingTilt = false;
int ldrTL, ldrTR, ldrBL, ldrBR;
void setup() {
Serial.begin(9600);
pinMode(PIN_BTN_RED, INPUT); pinMode(PIN_BTN_GREEN, INPUT);
pinMode(PIN_LED_R, OUTPUT); pinMode(PIN_LED_G, OUTPUT); pinMode(PIN_LED_B, OUTPUT);
startup(); // Selbsttest & Initialposition
}
void loop() {
// Serielle Kommandos verarbeiten
if (Serial.available() > 0) {
int code = Serial.parseInt();
processSerial(code);
}
// Grüner Taster: Tracking starten
if (digitalRead(PIN_BTN_GREEN) == HIGH && !running) {
enableTracking(); trackEnabled = true;
}
// Roter Taster: Tracking stoppen
if (digitalRead(PIN_BTN_RED) == HIGH && running) {
disableTracking();
}
// Sensoren lesen
readLDRs();
float topAvg = avgTop(), botAvg = avgBottom();
float leftAvg = avgLeft(), rightAvg = avgRight();
if (running) {
if (trackEnabled) trackToLight(topAvg, botAvg, leftAvg, rightAvg);
else { smoothMoveTwist(); smoothMoveTilt(); }
}
delay(GIVE_BACK_TIME);
}
// --- Tracking-Logik ---
void trackToLight(float top, float bot, float left, float right) {
const int STEP = 5; // Grad pro Zyklus
int twist = servoTwist.read();
int tilt = servoTilt.read();
if (left > right) twist += STEP; else if (right > left) twist -= STEP;
if (top > bot) tilt += STEP; else if (bot > top) tilt -= STEP;
twist = constrain(twist, 0, 179);
tilt = constrain(tilt, 45, 135); // Mechanischer Begrenzung
servoTwist.write(twist);
servoTilt.write(tilt);
if (verbose) {
Serial.print("LDRs: "); Serial.print(ldrTL); Serial.print(","); Serial.print(ldrTR);
Serial.print(","); Serial.print(ldrBL); Serial.print(","); Serial.println(ldrBR);
Serial.print("Avgs: T="); Serial.print(top); Serial.print(" B="); Serial.print(bot);
Serial.print(" L="); Serial.print(left); Serial.print(" R="); Serial.println(right);
Serial.print("Move: Twist="); Serial.print(twist); Serial.print(" Tilt="); Serial.println(tilt);
}
}
// --- Sanfte Servobewegung (Ramp) ---
void smoothMoveTwist() {
const int STEP = 1;
int cur = servoTwist.read();
if (cur == cmdTwistPos) { movingTwist = false; return; }
movingTwist = true;
int next = (cur < cmdTwistPos) ? cur + STEP : cur - STEP;
next = constrain(next, 0, 179);
servoTwist.write(next);
if (verbose) { Serial.print("Twist step: "); Serial.print(next); Serial.print("/"); Serial.println(cmdTwistPos); }
}
void smoothMoveTilt() {
const int STEP = 1;
int cur = servoTilt.read();
if (cur == cmdTiltPos) { movingTilt = false; return; }
movingTilt = true;
int next = (cur < cmdTiltPos) ? cur + STEP : cur - STEP;
next = constrain(next, 0, 179);
servoTilt.write(next);
if (verbose) { Serial.print("Tilt step: "); Serial.print(next); Serial.print("/"); Serial.println(cmdTiltPos); }
}
// --- Sensor-Hilfsfunktionen ---
void readLDRs() {
ldrTL = analogRead(PIN_LDR_TL); ldrTR = analogRead(PIN_LDR_TR);
ldrBL = analogRead(PIN_LDR_BL); ldrBR = analogRead(PIN_LDR_BR);
}
float avgTop() { return (ldrTL + ldrTR) / 2.0; }
float avgBottom() { return (ldrBL + ldrBR) / 2.0; }
float avgLeft() { return (ldrTL + ldrBL) / 2.0; }
float avgRight() { return (ldrTR + ldrBR) / 2.0; }
// --- Serielle Kommando-Verarbeitung ---
// Format: CCVVV (CC=Kommando-Kategorie 10/11/12, VVV=Wert 0-179)
void processSerial(int code) {
int cat = code / 1000;
int val = code % 1000;
Serial.print("Cmd: "); Serial.print(code); Serial.print(" Cat: "); Serial.print(cat); Serial.print(" Val: "); Serial.println(val);
if (cat == 10) { // System
switch (val) {
case 0: verbose = true; digitalWrite(PIN_LED_B, HIGH); Serial.println("Diag ON"); break;
case 1: verbose = false; digitalWrite(PIN_LED_B, LOW); Serial.println("Diag OFF"); break;
case 2: reportInfo(); break;
case 3: digitalWrite(PIN_LED_R, HIGH); break; // Rot an
case 4: digitalWrite(PIN_LED_R, LOW); break; // Rot aus
case 5: digitalWrite(PIN_LED_G, HIGH); break; // Grün an
case 6: digitalWrite(PIN_LED_G, LOW); break; // Grün aus
case 7: digitalWrite(PIN_LED_B, HIGH); break; // Blau an
case 8: digitalWrite(PIN_LED_B, LOW); break; // Blau aus
case 9: Serial.print("LDR TL: "); Serial.println(ldrTL); break;
case 10: Serial.print("LDR TR: "); Serial.println(ldrTR); break;
case 11: Serial.print("LDR BL: "); Serial.println(ldrBL); break;
case 12: Serial.print("LDR BR: "); Serial.println(ldrBR); break;
case 13: enableTracking(); break;
case 14: disableTracking(); break;
case 19: if (running && !movingTwist && !movingTilt) { cmdTwistPos=90; cmdTiltPos=90; Serial.println("Home 90°"); } break;
case 21: if (running) { trackEnabled = true; Serial.println("Tracking ON"); } break;
case 22: trackEnabled = false; Serial.println("Tracking OFF"); break;
default: Serial.print("Invalid val: "); Serial.println(val);
}
} else if (cat == 11) { // Twist-Servo direkt
if (val >= 0 && val <= 179 && running && !movingTwist) {
cmdTwistPos = val; movingTwist = true;
Serial.print("Twist cmd: "); Serial.println(val);
} else Serial.println("Invalid twist val");
} else if (cat == 12) { // Tilt-Servo direkt
if (val >= 0 && val <= 179 && running && !movingTilt) {
cmdTiltPos = val; movingTilt = true;
Serial.print("Tilt cmd: "); Serial.println(val);
} else Serial.println("Invalid tilt val");
}
Serial.println("-----");
}
// --- Tracking Ein/Aus ---
void enableTracking() {
running = true;
servoTwist.attach(PIN_SERVO_TWIST);
servoTilt.attach(PIN_SERVO_TILT);
digitalWrite(PIN_LED_G, HIGH); digitalWrite(PIN_LED_R, LOW);
Serial.println("Servos enabled");
}
void disableTracking() {
running = false; trackEnabled = false;
servoTwist.detach(); servoTilt.detach();
cmdTwistPos = servoTwist.read(); movingTwist = false;
cmdTiltPos = servoTilt.read(); movingTilt = false;
digitalWrite(PIN_LED_R, HIGH); digitalWrite(PIN_LED_G, LOW);
Serial.println("Servos disabled");
}
// --- Startup-Sequenz (Selbsttest) ---
void startup() {
int d = 500;
reportInfo(); delay(d);
processSerial(10003); delay(d); // Rot an
processSerial(10004); processSerial(10005); delay(d); // Rot aus, Grün an
processSerial(10006); processSerial(10007); delay(d); // Grün aus, Blau an
processSerial(10008); processSerial(10009); processSerial(10010); processSerial(10011); processSerial(10012); delay(d); // LDRs anzeigen
enableTracking(); delay(d);
processSerial(10019); delay(d); // Home 90°
disableTracking();
Serial.println("Startup done");
}
void reportInfo() {
Serial.println("Q2 Solar Tracker v2"); Serial.println("Tucson, AZ USA"); Serial.println("May 2018");
Serial.println("Checksum 58BA-D969-2F82-08FD-2078-2777-396D-E1AA");
}
3D-Druck / Mechanik
Montage-Details & Halterungszeichnungen als ZIP: q2_assembly_details_RCbi893qzA.zip
Schaltplan
Fritzing-Datei: q2_schematic_TVp7kakwNa.fzz
Herstellungsprozess
- Solarheizsysteme: Von passiven zu aktiven Lösungen für nachhaltiges Heizen
- Solarzellen: Von frühen Experimenten zur modernen Photovoltaik – Technologie, Produktion und Ausblick
- Echtzeit-Solarmonitoring mit Arduino: Low-Cost Datenerfassungssystem
- Intelligente Jalousien: Automatische Lichtsteuerung mit Arduino & Solarenergie
- IoT-Solartracker: Intelligentes Energiemanagement mit Arduino Mega
- SmartPostBox: IoT-fähiges intelligentes Briefkasten-System
- Dual-Achsen-Solartracker mit Auto- & Manual-Modus – Effizientes Sonnen-Tracking
- Solarenergie nutzen: Ihr vollständiger Leitfaden zu sauberer, zuverlässiger Energie
- Solarmodul-Qualität: Kritische Kontrollpunkte für maximale Leistung
- Solarmodul-Fertigung: Materialien, Maschinen und Prozessschritte