#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <IRremote.hpp>
#include <RTClib.h>
// LCD
LiquidCrystal_I2C lcd(0x27, 20, 4);
// RTC (DS1307 for simulation)
RTC_DS1307 rtc;
// IR
#define IR_RECEIVE_PIN 7
// IR Remote codes
#define BTN_UP 0x18 // 2
#define BTN_DOWN 0x4A // 8
#define BTN_LEFT 0x10 // 4
#define BTN_RIGHT 0x5A // 6
#define BTN_OK 0xA8 // OK or PLAY in Wokwi
// Photodiodes
const int pdTop = A0;
const int pdBottom = A1;
const int pdLeft = A2;
const int pdRight = A3;
// Stepper X (horizontal)
#define DIR_X 2
#define STEP_X 3
// Stepper Y (vertical)
#define DIR_Y 4
#define STEP_Y 5
// Settings
int delayTime = 3; // stepping delay
int stepSize = 10; // degrees per manual press
int threshold = 100; // photodiode light difference threshold
int lightThreshold = 200; // if all photodiodes < this, fallback to RTC
bool autoMode = true;
void setup() {
Serial.begin(9600);
lcd.begin(20, 4);
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Solar Tracker Ready");
// Initialize IR receiver
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
// Setup pins
pinMode(DIR_X, OUTPUT);
pinMode(STEP_X, OUTPUT);
pinMode(DIR_Y, OUTPUT);
pinMode(STEP_Y, OUTPUT);
// RTC start
if (!rtc.begin()) {
Serial.println("Couldn't find RTC!");
lcd.setCursor(0, 1);
lcd.print("RTC Not Found");
}
if (!rtc.isrunning()) {
Serial.println("RTC is NOT running, setting to compile time");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
}
void loop() {
if (IrReceiver.decode()) {
uint32_t value = IrReceiver.decodedIRData.command;
Serial.print("IR Code: 0x"); Serial.println(value, HEX);
if (value == BTN_OK) {
autoMode = !autoMode;
lcd.setCursor(0, 2);
lcd.print("Mode: ");
lcd.print(autoMode ? "AUTO " : "MANUAL");
}
if (!autoMode) {
if (value == BTN_UP) moveStepper(STEP_Y, DIR_Y, HIGH, stepSize);
if (value == BTN_DOWN) moveStepper(STEP_Y, DIR_Y, LOW, stepSize);
if (value == BTN_LEFT) moveStepper(STEP_X, DIR_X, LOW, stepSize);
if (value == BTN_RIGHT) moveStepper(STEP_X, DIR_X, HIGH, stepSize);
}
IrReceiver.resume(); // ready to receive next
}
if (autoMode) {
int top = analogRead(pdTop);
int bottom = analogRead(pdBottom);
int left = analogRead(pdLeft);
int right = analogRead(pdRight);
lcd.setCursor(0, 1);
lcd.print("T:");
lcd.print(top);
lcd.print(" B:");
lcd.print(bottom);
lcd.setCursor(0, 3);
lcd.print("L:");
lcd.print(left);
lcd.print(" R:");
lcd.print(right);
bool lowLight = (top < lightThreshold && bottom < lightThreshold &&
left < lightThreshold && right < lightThreshold);
if (lowLight) {
// Use RTC to control
DateTime now = rtc.now();
int hour = now.hour();
int minute = now.minute();
// Horizontal movement by hour
int hourSteps = map(hour, 6, 18, 0, 200); // 6 AM to 6 PM
moveToPosition(STEP_X, DIR_X, hourSteps);
// Vertical movement by minute (optional)
int minuteSteps = map(minute, 0, 59, 0, 100);
moveToPosition(STEP_Y, DIR_Y, minuteSteps);
} else {
// Top/Bottom
if (abs(top - bottom) > threshold) {
if (top > bottom)
moveStepper(STEP_Y, DIR_Y, LOW, 1);
else
moveStepper(STEP_Y, DIR_Y, HIGH, 1);
}
// Left/Right
if (abs(left - right) > threshold) {
if (left > right)
moveStepper(STEP_X, DIR_X, LOW, 1);
else
moveStepper(STEP_X, DIR_X, HIGH, 1);
}
}
}
delay(100);
}
void moveStepper(int stepPin, int dirPin, bool direction, int steps) {
digitalWrite(dirPin, direction);
for (int i = 0; i < steps; i++) {
digitalWrite(stepPin, HIGH);
delay(delayTime);
digitalWrite(stepPin, LOW);
delay(delayTime);
}
}
void moveToPosition(int stepPin, int dirPin, int targetStep) {
// You can implement this based on a position tracking system if needed
// For now, this function is a placeholder
}
UP
LEFT
RIGHT
DOWN
X - AXIS
(HORIZONTAL)
Y - AXIS
(VERTICAL)