Smart Solar Tracker - Arduino Solar Panel System

by charlieqian222333 in Circuits > Arduino

206 Views, 1 Favorites, 0 Comments

Smart Solar Tracker - Arduino Solar Panel System

Smart Solar Tracker
IMG_2198.JPG

This project for IEEE Arduino Contest 2024 is all about creating a solar tracking system that maximizes energy efficiency by capturing the most sunlight, which is realized by adjusting the position of the panel automatically, given limited electronic components allowed to use. I wrote it in a way where users can switch between manual control, automatic scanning, and remote control through a custom interface.

A YouTube video overviewing this project for the contest can be found under my channel @charlieqian6770.

https://www.youtube.com/watch?v=3B3VpSgj0tg

Supplies

IMG_2151.JPG
IMG_2155.JPG
IMG_2149.JPG
IMG_2156.JPG

System Configuration:

  1. Arduino MEGA controller board 1pc
  2. LCD1602 Module (with pin header) 1pc
  3. Servo Motor SG90 1pc
  4. DHT11 Temperature and Humidity Module 1pc
  5. Potentiometer 10K 2pcs
  6. 830 Tie-Points Breadboard 1pc
  7. Buttons 2pcs
  8. Resistors
  9. Photoresistor 1pc
  10. Red LED, blue LED, and green LED 1pc each
  11. Breadboard jumper wires
  12. Female-to-male wires


Other materials for demonstration and test purposes:

  1. 2D plastic platform 1pc
  2. Foam plastic
  3. Aluminum kitchen foil
  4. A stable and centralized light source
  5. USB cable 1pc


Hardware and software:

  1. A laptop
  2. The Arduino IDE
  3. The processing interface


More about Arduino mega: "ELEGOO Mega 2560 Project The Most Complete Ultimate Starter Kit"

Fundamental Logic and Initialization

IMG_2191.JPG
IMG_2196.JPG
IMG_2197.JPG

The potentiometer is used to control that servo motor. As you twist the potentiometer, the Arduino reads the analog value and maps it to a corresponding Servo angle, which is then displayed on the LCD screen.

This part initializes the libraries for controlling servo motors, interfacing with LCD displays, and interfacing with DHT sensors.

#include <Servo.h>
#include <LiquidCrystal.h>
#include <DHT.h>


We also need to define the pin locations.


A brief overview of the three control modes for this tracker:

1. Manual Mode

  1. The user will manually controls the movement of the panel using a potentiometer. This mode is activated when Button 1 is pressed.

2. Processing Mode

  1. This mode is activated when the system receives a command from Processing software, which allow external control of the solar panel. Button 2 or a serial input ("P") activates this mode.

3. Automatic Scanning Mode

  1. The solar panel will automatically scans for the best position based on light intensity measured by an Light Dependent Resistor (LDR). It will then move to the position with the highest detected light intensity, and importantly, periodic scans will occur if the environment changes significantly (either temperature or light intensity, monitored by a DHT 11 sensor).


We need to complete Button Debounce, and Servo/ LCD objects declaration, as well.


Threshold value setup - you can play around with this critical value so that it truly reflects optimal changes in later modes.

const float tempThreshold = 0.2; // Temperature change threshold
const float lightThreshold = 50; // Light intensity change threshold


boolean debounce(boolean last, int pin) {
boolean current = digitalRead(pin);
if (last != current) {
delay(5);
current = digitalRead(pin);}
return current;}


Nuances like serial communication setup, initializations, and LCD welcome message are omitted here.


Manual Control

IMG_2183.JPG
ab20cce95ec880251d4f196b0e755a8.png

Button 1 - Manual Mode Activation

// Read the current state of the buttons
currentButton1 = debounce(lastButton1, button1Pin);
if (lastButton1 == LOW && currentButton1 == HIGH) {
ledOn3 = false;
ledOn1 = !ledOn1;
ledOn2 = !ledOn2;
manualMode = !manualMode;
useProcessing = !useProcessing;
}
lastButton1 = currentButton1;
digitalWrite(LED1, ledOn1);

Comments:

  1. The reads the state of Button 1 using a debounce function to prevent false presses due to likely mechanical bounce for smooth operation.
  2. The state of the corresponding LED is updated to reflect current mode.
  3. When pressed, Button 1 allows manual mode overring any automatic or processing-based control.


Manual Mode Servo Control

if (manualMode) {
// Manual mode: control servo with potentiometer
int potValue = analogRead(potPin);
int servoPos = map(potValue, 0, 1023, 0, 180);
panelServo.write(servoPos);

Comments:

  1. When manual mode is active (manualMode == true), the system reads the value from the potentiometer connected to potPin.
  2. The potentiometer value (0 to 1023) is mapped to a range suitable for the servo motor (0° to 180°), which allows precise control of the solar panel position.
  3. The servo position is updated in real-time based on the potentiometer’s position.
  4. The LCD displays the current mode as "Manual Mode" and the current position of the servo.
  5. The map() function ensures that the potentiometer has the full range.

LEDs for Visual Feedback

digitalWrite(LED1, ledOn1);

Comments:

  1. In manual mode, LED1 is turned on; in any other mode, it will be turned off.
  2. For visual confirmation for user accessibility.

Button 2 - Reset to Manual Mode

currentButton2 = debounce(lastButton2, button2Pin);
if (lastButton2 == LOW && currentButton2 == HIGH) {
ledOn3 = false;
ledOn2 = false;
ledOn1 = true;
manualMode = true;
}
lastButton2 = currentButton2;
digitalWrite(LED2, ledOn2);

Comments:

  1. It allows the user to return to manual mode by pressing Button 2 from other modes by overriding.

Remote Control Via Processing Interface

870ef6a8ee3e13cf9f4bb7f84df496f.png
IMG_2196.JPG
IMG_2206.JPG
IMG_2184.JPG
IMG_2159.JPG
IMG_2161.JPG

Part 1/2 - Arduino IDE Code


Task A Serial Input to Trigger Processing Mode

Comments:

  1. When this code detects the character "P", Processing Mode is activated by setting useProcessing to true and manualMode to false.
  2. If the character "A" is received, the systems switches to automatic mode.
  3. In Processing Mode, the code will read servo position values sent via the serial connection and adjust the servo based on where the user is going to click on the interface map where sunlight intensity is laid.


Task B Processing Mode Servo Control

else if (useProcessing) {
lcd.setCursor(0, 0);
lcd.print("Processing Mode ");
lcd.setCursor(0, 1);
lcd.print("Servo Pos: ");
lcd.print(panelServo.read());
}


Task C LEDs for Visual Feedback


Task D Resetting to Manual or Automatic Mode






Part 2/2 - Processing Code


Task E Import and Serial Initialization and Grid Setup Variables

import processing.serial.*;
Serial myPort;
int cols, rows;
int rectSize = 50; // Size of each rectangle in the grid
void setup() {
size(800, 600);
String portName = Serial.list()[0]; // Adjust the index based on your port
myPort = new Serial(this, portName, 9600);
cols = width / rectSize;
rows = height / rectSize;
noLoop(); // Ensure draw() is called only once unless explicitly called
}

Comments:

  1. Several things are achieved here. First off, a serial communication between the Arduino and the Processing is established through the declaration of a Serial object.
  2. Secondly, cols, rows, and rectSize define the number of columns, rows and the size of the rectangle of the grid generated, respectively. This will visualize the intensity map.


Task F Setup Function

Comments:

  1. size(800, 600) sets the window dimensions to 800*600 pixels.
  2. Serial.list()[0] obtains the first available serial port.
  3. myPort initializes the serial communication on portName at a baud rate of 9600.
  4. noLoop() will make draw() run only once, which only allows the interface to refresh when redraw() is called.


Several Important Functions to Outline:

Task G Draw function

void draw() {
background(255);
generateRandomIntensityMap();
drawLegend();
}


Task H Generating the Random Intensity Map Function

void generateRandomIntensityMap() {
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
float randomBrightness = random(0, 255); // Random brightness between 0 and 255
color intensityColor = colorMap(randomBrightness, 0, 255);
fill(intensityColor);
rect(i * rectSize, j * rectSize, rectSize, rectSize);
}
}
}


Task I Color mapping function

color colorMap(float value, float start1, float stop1) {
float normalized = map(value, start1, stop1, 0, 1);
return lerpColor(color(0, 0, 255), color(255, 0, 0), normalized); // Blue to red gradient
}


Task J Drawing the legend

void drawLegend() {
int legendWidth = 20;
for (int i = 0; i <= 255; i++) {
float normalized = map(i, 0, 255, 0, 1);
color intensityColor = lerpColor(color(0, 0, 255), color(255, 0, 0), normalized);
fill(intensityColor);
rect(width - legendWidth, map(i, 0, 255, height, 0), legendWidth, height / 255.0);
}
fill(0);
textSize(16);
text("Low", width - legendWidth - 40, height - 10);
text("High", width - legendWidth - 40, 20);
}


Task K Mouse and keyboard interactions


Automatic Scanning

IMG_2167.JPG
2f20028a35ac7f3a3932c82ff5fd2e0.png
IMG_2169.JPG
IMG_2171.JPG
IMG_2172.JPG
IMG_2205.JPG
IMG_2212.JPG
1d7c7d48774425a9b39ddba93e3c216.jpg
3d2338913355bfbf8096ee80c5b1f56.jpg

Automatic scanning mode initialization


Initial scan for optimal light position

else {
if (ledOn3 == false) {
maxLdrValue = 0; // Reset the maximum light intensity value
for (int pos = 0; pos <= scanRange; pos += stepSize) {
panelServo.write(pos); // Move the servo to the current position
delay(500); // Wait for the servo to reach the position and stabilize
ldrValue = analogRead(ldrPin); // Read the light intensity value
if (ldrValue > maxLdrValue) { // Check if this is the highest light intensity found
maxLdrValue = ldrValue; // Update the maximum light intensity value
bestServoPos = pos; // Update the best servo position
}
}

Comments:

  1. The system performs an initial scan across a specified range (scanRange) when first activated at each step.
  2. The servo moves based on the stepSize across the range from the LDR values.
  3. If it stores the highest light intensity value (maxLdrValue) and the position at that scan (bestServoPos), it will be set as the panel's 'optimal' position.


Panel position adjustment based on environmental changes

else {
if (ledOn3 == false) {
maxLdrValue = 0; // Reset the maximum light intensity value
for (int pos = 0; pos <= scanRange; pos += stepSize) {
panelServo.write(pos); // Move the servo to the current position
delay(500); // Wait for the servo to reach the position and stabilize

ldrValue = analogRead(ldrPin); // Read the light intensity value
if (ldrValue > maxLdrValue) { // Check if this is the highest light intensity found
maxLdrValue = ldrValue; // Update the maximum light intensity value
bestServoPos = pos; // Update the best servo position
}
// Move the panel to the best position found
panelServo.write(bestServoPos);
ledOn3 = true;
}
}

Comments:

  1. The system will continuously monitors light intensity and temperature in this mode.
  2. If the changes in temperature and light exceed the defined thresholds (i.e. Current panel position could no longer be at the position where sunlight is maximum), the system triggers a new scan.
  3. The LCD will display the current temperature and light intensity for user as a real-time feedback.


Testing, Results and Summary

3324916f069e9d4c9c3ca43eec1d4fc.jpg
373e19002c60c9ad7743473aae4dd52.jpg
IMG_2136.JPG
IMG_2137.JPG
IMG_2138.JPG
IMG_2140.JPG
IMG_2148.JPG

After assembling (this could be tricky when you are attempting to wrap your carefully pre-holed foam block with foil into a perfect cuboid) and calibrating the solar tracker which will be placed on a 2-direction electro-mechanical head supporter (this could be tricky when you are attempting to determine the angle 0 position of the motor to make sure your panel installed later on will not crash into the supported due to angle miscalculations).


Overall, each mode demonstrated its unique advantages depending on the environmental conditions. This overall meets my initial expectation of building a all-around solar energy capture system, which can be potentially produced largely and function well in all terrains and contexts. Control Mode 3 - the automatic scanning mode - achieved the most optimal energy yield under variable sunlight. Up to 25% more of solar power can be collected comparing to other configurations.


To conclude, what this project can bring you (it definitely brought me!) are the following: real-world applications, optimizing hardware-software interaction, and troubleshooting hardware issues. Again, due to the nature of this competition, these skills are critical when you are trying to innovate something purposeful, helpful and situational.


In the future where constraints will be minimized, I will suggest investigators to study how a 3D supporter with 2+ servos will benefit the accuracy and operability of the system.

Source Code

Downloads