Точная солнеследующая платформа на сервоприводах для максимальной генерации энергии
Компоненты и материалы
| Фоторезисторы (LDR) | 4 шт. |
| RGB NeoPixel LED (4 шт. в наборе Adafruit Flora или аналогичные) | 1 набор |
| Тактильные кнопки 12 мм (нормально открытые) | 2 шт. |
| Датчик температуры (например, TMP36 или LM35) | 1 шт. |
| Arduino UNO | 1 шт. |
| Сервоприводы 180° (Futaba S3003 или RobotGeek) | 2 шт. |
| Резисторы 10 кОм | 6 шт. |
| Резистор 220 Ом (для LED) | 1 шт. |
| Конденсаторы 100 мкФ | 2 шт. |
| Солнечная панель ~80×80 мм | 1 шт. |
Инструменты и механика
- Ручной инструмент, мелкие болты/гайки для крепления
- Твёрдая база для основания (деревянный брусок)
- Алюминиевый профиль ~3 мм толщиной, <2 см шириной — для L-образного кронштейна (ось «twist»)
- Тонкая сталь с отверстиями (кронштейны для вентиляции) — для крепления панели к сервоприводу «tilt»
Программное обеспечение
- Arduino IDE
- Microsoft Excel (документация, логи)
О проекте
Ещё одна реализация двухосевого солнеслежения на двух сервоприводах 180° и четырёх фоторезисторах. Платформа с панелью и датчиками поворачивается к зоне максимальной освещённости.
Есть RGB-индикатор состояния (включено/выключено) и две кнопки: включение/выключение слежения. Команды также можно посылать через Serial-порт (числовые коды). Реализовано плавное движение сервоприводов (пошаговые приращения) вместо рывков.
Код (Arduino, версия Q2 v2.0)
// Scott Mangiacotti, Tucson, Arizona, May 2018
#include
const int GIVE_BACK_TIME = 125; // мс между итерациями loop()
// Входы
const int I_RED_BUTTON_PIN = 4; // Выкл
const int I_GREEN_BUTTON_PIN = 2; // Вкл
const int I_LDR1_PIN = A3; // Верхний левый
const int I_LDR2_PIN = A2; // Верхний правый
const int I_LDR3_PIN = A1; // Нижний левый
const int I_LDR4_PIN = A0; // Нижний правый
// Выходы
const int O_RED_LED_PIN = 9;
const int O_GREEN_LED_PIN = 11;
const int O_BLUE_LED_PIN = 10;
const int O_TWIST_SERVO_PIN = 5; // Азимут
const int O_TILT_SERVO_PIN = 6; // Угол места
// Глобальные переменные
bool gRunning = false;
bool gTrackToLightEnabled = false;
bool gVerboseDiagMode = false;
Servo gServoTwist;
int gServoTwistPositionCommand;
bool gServoTwistMoveIP;
Servo gServoTilt;
int gServoTiltPositionCommand;
bool gServoTiltMoveIP;
int gLDR1, gLDR2, gLDR3, gLDR4;
void setup() {
Serial.begin(9600);
pinMode(I_RED_BUTTON_PIN, INPUT);
pinMode(I_GREEN_BUTTON_PIN, INPUT);
pinMode(O_RED_LED_PIN, OUTPUT);
pinMode(O_GREEN_LED_PIN, OUTPUT);
pinMode(O_BLUE_LED_PIN, OUTPUT);
startup();
}
void loop() {
// Serial-команды
if (Serial.available() > 0) {
int iControlCode = Serial.parseInt();
processSerialMessage(iControlCode);
}
// Зелёная кнопка — включить слежение
if (digitalRead(I_GREEN_BUTTON_PIN) == HIGH && !gRunning) {
enableTracking(); gTrackToLightEnabled = true;
}
// Красная кнопка — выключить
if (digitalRead(I_RED_BUTTON_PIN) == HIGH && gRunning) {
disableTracking();
}
readPhotoResistors();
if (gRunning) {
if (gTrackToLightEnabled) {
trackToLightSensors(); // Автоматическое слежение
} else {
smoothMoveTwist(); // Ручное управление через Serial
smoothMoveTilt();
}
}
delay(GIVE_BACK_TIME);
}
void enableTracking() {
gRunning = true;
gServoTwist.attach(O_TWIST_SERVO_PIN);
gServoTilt.attach(O_TILT_SERVO_PIN);
digitalWrite(O_GREEN_LED_PIN, HIGH);
digitalWrite(O_RED_LED_PIN, LOW);
Serial.println("servos enabled");
}
void disableTracking() {
gRunning = false; gTrackToLightEnabled = false;
gServoTwist.detach(); gServoTilt.detach();
gServoTwistPositionCommand = gServoTwist.read(); gServoTwistMoveIP = false;
gServoTiltPositionCommand = gServoTilt.read(); gServoTiltMoveIP = false;
digitalWrite(O_RED_LED_PIN, HIGH);
digitalWrite(O_GREEN_LED_PIN, LOW);
Serial.println("servos disabled");
}
void trackToLightSensors() {
float fTop = averageTopTwoSensors();
float fBottom = averageBottomTwoSensors();
float fLeft = averageLeftTwoSensors();
float fRight = averageRightTwoSensors();
int iTwistMoveCommand = gServoTwist.read();
int iTiltMoveCommand = gServoTilt.read();
int iMoveAmount = 5; // градусов за шаг (в сочетании с GIVE_BACK_TIME задаёт скорость)
if (fLeft > fRight) iTwistMoveCommand += iMoveAmount;
else if (fRight > fLeft) iTwistMoveCommand -= iMoveAmount;
if (fTop > fBottom) iTiltMoveCommand += iMoveAmount;
else if (fBottom > fTop) iTiltMoveCommand -= iMoveAmount;
// Ограничители
iTwistMoveCommand = constrain(iTwistMoveCommand, 0, 179);
iTiltMoveCommand = constrain(iTiltMoveCommand, 45, 135);
gServoTwist.write(iTwistMoveCommand);
gServoTilt.write(iTiltMoveCommand);
if (gVerboseDiagMode) {
Serial.print("LDRs: "); Serial.print(gLDR1); Serial.print(","); Serial.print(gLDR2);
Serial.print(","); Serial.print(gLDR3); Serial.print(","); Serial.println(gLDR4);
Serial.print("Avgs: T="); Serial.print(fTop); Serial.print(" B="); Serial.print(fBottom);
Serial.print(" L="); Serial.print(fLeft); Serial.print(" R="); Serial.println(fRight);
Serial.print("Cmd: Twist="); Serial.print(iTwistMoveCommand); Serial.print(" Tilt="); Serial.println(iTiltMoveCommand);
}
}
void readPhotoResistors() {
gLDR1 = analogRead(I_LDR1_PIN);
gLDR2 = analogRead(I_LDR2_PIN);
gLDR3 = analogRead(I_LDR3_PIN);
gLDR4 = analogRead(I_LDR4_PIN);
}
// Плавное движение сервопривода "twist" (азимут)
void smoothMoveTwist() {
const int iMoveAmountPerScan = 1;
int iCurrentPos = gServoTwist.read();
if (iCurrentPos == gServoTwistPositionCommand) { gServoTwistMoveIP = false; return; }
gServoTwistMoveIP = true;
int iNewMoveCommand = iCurrentPos;
if (iCurrentPos < gServoTwistPositionCommand) iNewMoveCommand += iMoveAmountPerScan;
else iNewMoveCommand -= iMoveAmountPerScan;
iNewMoveCommand = constrain(iNewMoveCommand, 0, 179);
gServoTwist.write(iNewMoveCommand);
if (gVerboseDiagMode) { Serial.print("Twist move: "); Serial.print(iNewMoveCommand); Serial.print(" target: "); Serial.println(gServoTwistPositionCommand); }
}
// Плавное движение сервопривода "tilt" (угол места)
void smoothMoveTilt() {
const int iMoveAmountPerScan = 1;
int iCurrentPos = gServoTilt.read();
if (iCurrentPos == gServoTiltPositionCommand) { gServoTiltMoveIP = false; return; }
gServoTiltMoveIP = true;
int iNewMoveCommand = iCurrentPos;
if (iCurrentPos < gServoTiltPositionCommand) iNewMoveCommand += iMoveAmountPerScan;
else iNewMoveCommand -= iMoveAmountPerScan;
iNewMoveCommand = constrain(iNewMoveCommand, 0, 179);
gServoTilt.write(iNewMoveCommand);
if (gVerboseDiagMode) { Serial.print("Tilt move: "); Serial.print(iNewMoveCommand); Serial.print(" target: "); Serial.println(gServoTiltPositionCommand); }
}
float averageTopTwoSensors() { return (gLDR1 + gLDR2) / 2.0; }
float averageBottomTwoSensors() { return (gLDR3 + gLDR4) / 2.0; }
float averageLeftTwoSensors() { return (gLDR1 + gLDR3) / 2.0; }
float averageRightTwoSensors() { return (gLDR2 + gLDR4) / 2.0; }
// Обработка Serial-команд: код = команда*1000 + значение
void processSerialMessage(int iControlCode) {
int iControlCommand = iControlCode / 1000;
int iControlValue = iControlCode % 1000;
Serial.print("code: "); Serial.println(iControlCode);
Serial.print("cmd: "); Serial.println(iControlCommand);
Serial.print("val: "); Serial.println(iControlValue);
if (iControlCommand == 10) { // Разное
switch (iControlValue) {
case 0: gVerboseDiagMode = true; digitalWrite(O_BLUE_LED_PIN, HIGH); Serial.println("diag ON"); break;
case 1: gVerboseDiagMode = false; digitalWrite(O_BLUE_LED_PIN, LOW); Serial.println("diag OFF"); break;
case 2: reportProductInfo(); break;
case 3: digitalWrite(O_RED_LED_PIN, HIGH); Serial.println("red ON"); break;
case 4: digitalWrite(O_RED_LED_PIN, LOW); Serial.println("red OFF"); break;
case 5: digitalWrite(O_GREEN_LED_PIN, HIGH); Serial.println("green ON"); break;
case 6: digitalWrite(O_GREEN_LED_PIN, LOW); Serial.println("green OFF"); break;
case 7: digitalWrite(O_BLUE_LED_PIN, HIGH); Serial.println("blue ON"); break;
case 8: digitalWrite(O_BLUE_LED_PIN, LOW); Serial.println("blue OFF"); break;
case 9: Serial.print("LDR1: "); Serial.println(gLDR1); break;
case 10: Serial.print("LDR2: "); Serial.println(gLDR2); break;
case 11: Serial.print("LDR3: "); Serial.println(gLDR3); break;
case 12: Serial.print("LDR4: "); Serial.println(gLDR4); break;
case 13: enableTracking(); break;
case 14: disableTracking(); break;
case 19: if (gRunning && !gServoTwistMoveIP && !gServoTiltMoveIP) { gServoTwistPositionCommand = 90; gServoTiltPositionCommand = 90; Serial.println("home 90/90"); } break;
case 21: if (gRunning) { gTrackToLightEnabled = true; Serial.println("track ON"); } break;
case 22: gTrackToLightEnabled = false; Serial.println("track OFF"); break;
default: Serial.print("bad val: "); Serial.println(iControlValue);
}
} else if (iControlCommand == 11) { // Servo Twist
if (iControlValue >= 0 && iControlValue <= 179 && gRunning && !gServoTwistMoveIP) {
gServoTwistPositionCommand = iControlValue; gServoTwistMoveIP = true;
Serial.print("Twist cmd: "); Serial.println(gServoTwistPositionCommand);
} else Serial.print("bad val: "); Serial.println(iControlValue);
} else if (iControlCommand == 12) { // Servo Tilt
if (iControlValue >= 0 && iControlValue <= 179 && gRunning && !gServoTiltMoveIP) {
gServoTiltPositionCommand = iControlValue; gServoTiltMoveIP = true;
Serial.print("Tilt cmd: "); Serial.println(gServoTiltPositionCommand);
} else Serial.print("bad val: "); Serial.println(iControlValue);
}
Serial.println("-----");
}
void startup() {
int d = 500;
reportProductInfo(); delay(d);
processSerialMessage(10003); delay(d); // red ON
processSerialMessage(10004); processSerialMessage(10005); delay(d); // red OFF, green ON
processSerialMessage(10006); processSerialMessage(10007); delay(d); // green OFF, blue ON
processSerialMessage(10008); processSerialMessage(10009); processSerialMessage(10010); processSerialMessage(10011); processSerialMessage(10012); delay(d); // LDR values
enableTracking(); delay(d);
processSerialMessage(10019); delay(d); // home
disableTracking();
Serial.println("startup done");
}
void reportProductInfo() {
Serial.println("q version 2");
Serial.println("tucson, arizona usa");
Serial.println("may 2018");
Serial.println("checksum 58BA-D969-2F82-08FD-2078-2777-396D-E1AA");
}
3D-модели и чертежи
Архив с деталями сборки: q2_assembly_details_RCbi893qzA.zip
Схемы
Fritzing-файл: q2_schematic_TVp7kakwNa.fzz
Производственный процесс
- Солнечные отопительные системы: от пассивных к активным решениям для устойчивого отопления домов
- Солнечные ячейки: от ранних экспериментов к современной фотовольтаике — технологии, производство и перспекти…
- Мониторинг солнечной панели в реальном времени: дешёвая система сбора данных на Arduino и Excel
- Умные шторы: автоматическое управление светом на Arduino с солнечной энергией
- IoT-солнечный трекер на Arduino: умное управление энергией с мониторингом через Cayenne
- SmartPostBox: умный почтовый ящик на Arduino MKR Fox 1200 и Sigfox
- Двухосевой солнечный трекер с авто/ручным управлением
- Солнечная энергия: полное руководство по чистой и надежной энергии
- Качество солнечных панелей: ключевые контрольные точки для оптимальной производительности
- Как производятся солнечные панели: пошаговое руководство к высококачественному солнечному производству