Solar Tracker
Solar panel usage is increasing all across the world. Most areas use static solar panels which have an energy output based on the sun's location. Solar farms use solar trackers to achieve a constant energy output throughout the day. I created a solar tracker using an ESP32 microcontroller and a servo to compare the results with a static (non-moving) panel. Results were displayed on a local web server using the ESP32's WiFi capabilities. Components used included 2 x ESP32 TTGO T-Display, 2 x Photoresistor, 1 x Hobby servo, 1 x Lego servo mount, 2 x Breadboard, 2 x Solar panel, 4 x Resistors (Used in a voltage divider to reduce the 8.25V from the solar panel to a usable 3.3V for the ESP32 GPIOs.), A few jumper wires, Hot glue.
Left - Static panel. The static panel has a peak energy output at a specific position when the "sun" or flashlight used in the model was directly above the solar panel. When the "sun" was at other angles, energy absorption was severely limited.
Right - Solar tracker. The tracker had a constant energy output due to the photoresistors constantly updating the microcontroller on the position of the "sun". One photoresistor was placed on either side of the panel. Depending on which photoresistor was getting more light, the servo would rotate. The 2 transition points on the graph show the limitations of a model. Those were caused by the "sun's" rather fast movement which is much slower in contrast to the real world. If the "sun" was slower, the servo would have more time to react.
Code: (Path: ./tracker.ino)
#include <ESP32Servo.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include "WiFi.h"
TFT_eSPI tft = TFT_eSPI();
Servo myservo;
int L = 39;
int R = 33;
int LED = 27;
int rightD;
int leftD;
const char* ssid = "R63L2";
const char* password = "9030225696";
AsyncWebServer server(80);
void setup() {
tft.init();
// Serial port for debugging purposes
Serial.begin(115200);
pinMode(32, INPUT);
myservo.attach(17);
pinMode(L, INPUT);
pinMode(R, INPUT);
pinMode(LED, OUTPUT);
// Initialize SPIFFS
if (!SPIFFS.begin()) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
// Print ESP32 Local IP Address
Serial.println(WiFi.localIP());
tft.fillScreen(TFT_WHITE);
tft.setCursor(3, 3, 2);
tft.setTextColor(TFT_BLACK);
tft.setTextSize(1);
tft.println(WiFi.localIP());
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send(SPIFFS, "/index.html");
});
server.on("/voltage", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send_P(200, "text/plain", rawVoltage().c_str());
});
// Start server
server.begin();
}
String rawVoltage() {
int analogValServer = analogRead(32);
float rawVoltServer = analogValServer * (3.3 / 4095) * 2.5;
return String(rawVoltServer);
}
void moveL(int leftD) {
myservo.attach(17);
myservo.writeMicroseconds(1550);
delay(leftD);
myservo.detach();
}
void moveR(int rightD) {
myservo.attach(17);
myservo.writeMicroseconds(1450);
delay(rightD);
myservo.detach();
}
void loop() {
int analogVal = analogRead(32);
float rawVolt = analogVal * (3.3 / 4095) * 2.5;
float Lval = analogRead(L);
float Rval = analogRead(R);
float RbyLval = Rval / Lval;
float LbyRval = Lval / Rval;
Serial.print("Rval/Lval: ");
Serial.println(RbyLval);
Serial.print("Lval/Rval: ");
Serial.println(LbyRval);
while ((LbyRval <= 1.09 && LbyRval >= 0.95) && (RbyLval >= 0.95 && RbyLval <= 1.09)) {
digitalWrite(27, HIGH);
Serial.println("Loop 1");
delay(2000);
break;
}
// while (RbyLval <= 1.09 && LbyRval >= 0.95 and ) {
// digitalWrite(27, HIGH);
// Serial.println("Loop 2");
// delay(2000);
// break;
// }
if (LbyRval > 1.09 && RbyLval < 0.95) {
digitalWrite(27, LOW);
Serial.println("Loop 3");
moveR(200);
// delay(2000);
// break;
}
else if (RbyLval > 1.09 && LbyRval < 0.95) {
digitalWrite(27, LOW);
Serial.println("Loop 4");
moveL(200);
// delay(2000);
// break;
}
}
Webserver Code: (Path: ./data/index.html)
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://code.highcharts.com/highcharts.js"></script>
<style>
body {
min-width: 310px;
max-width: 800px;
height: 400px;
margin: 0 auto;
}
h2 {
font-family: Arial;
font-size: 2.5rem;
text-align: center;
}
</style>
</head>
<body>
<h2>ESP32 Solar Tracker</h2>
<div id="chart-voltage" class="container"></div>
</body>
<script>
var chartV = new Highcharts.Chart({
chart:{ renderTo : 'chart-voltage' },
title: { text: 'Solar Panel Voltage' },
series: [{
showInLegend: false,
data: []
}],
plotOptions: {
line: { animation: false,
dataLabels: { enabled: true }
},
series: { color: '#059e8a' }
},
xAxis: { type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: { text: 'Voltage (V)' }
//title: { text: 'Temperature (Fahrenheit)' }
},
credits: { enabled: false }
});
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = (new Date()).getTime(),
y = parseFloat(this.responseText);
//console.log(this.responseText);
if(chartV.series[0].data.length >2048) {
chartV.series[0].addPoint([x, y], true, true, true);
} else {
chartV.series[0].addPoint([x, y], true, false, true);
}
}
};
xhttp.open("GET", "/voltage", true);
xhttp.send();
}, 1000 ) ; // refresh rate
</script>
</html>
File Tree: