Skip to content

Machine Building Group 2

Project name

We named our solar tracker Areg (Արեգ), an Armenian male name that literally translates to Sun, perfectly reflecting the core purpose of our device

Slide and Video

slide

Members

Students

  • Gevorg Malkhasyan - Sensor PCB development, 3D modeling, 3D printing, Assembling, Coding

  • Ani Petrosyan - 3D modeling, 3D printing, Assembling, Coding

Instructors

Servo Motor Selection

After completing the PCB design phase, we moved on to selecting the motors. We needed to provide both vertical and horizontal rotation for the system.

Comparing Specifications

We considered two different servo motors.

Feature HK-15138 Servo Motor Corona DS-939MG Servo Motor
Image
Motor Type Standard analog servo Digital metal gear servo
Operating Voltage 4.8V – 6V 4.8V – 6V
Torque ~4.3 kg.cm ~2.5–2.7 kg.cm
Current ~0.7–1 A (load dependent) ~0.2–0.24 A (normal operation)
Speed ~0.17 sec/60° ~0.13–0.14 sec/60°
Gear Type Plastic gears Metal gears
Weight ~38 g ~12.5 g
Rotation Angle ~180° ~180°
Control Signal PWM PWM

As an initial criterion, we looked at the torque value. In the case of the Corona DS-939MG Servo Motor, the rated torque is ~2.5–2.7 kg·cm. This figure seemed promising, as we intended to keep our device as lightweight as possible. It means the servo can rotate a 250-gram load at an arm length of 10 cm, which is well suited for our purposes.

Servo Motor Test

We didn’t stop there — it was important for us to observe the servo motor’s behavior under load firsthand.

To do this, we used a laser cutter to cut a circular disc from 3 mm plywood with a diameter of 150 mm, which allowed us to place weights on it.

We then attached the disc to the servo motor and connected it to an Arduino UNO.

Test Code

For this test we used the official Arduino example from the Arduino servo motor documentation:

#include <Servo.h>

Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards

int pos = 0;    // variable to store the servo position

void setup() {
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
  for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
}

This code sweeps the servo continuously from 0° to 180° and back, allowing us to observe its behavior during repeated motion cycles:

Performance Under Load

After verifying the basic motion, we began applying various weights to the servo to simulate real operating conditions. By gradually increasing the load, we were able to assess whether the motor could reliably handle the mechanical demands of our sun-tracking system:

After testing, we measured the weight of the assembly using a scale. The total weight of the upper structure came to approximately 805 grams:

Servo load

Performance Under Vertical Load

We also tested an alternative load configuration, with the weight suspended vertically:

Final Selection

Based on the test results, it was clear that the servo motor could operate stably and provide the necessary range of motion even under load. These findings informed our final motor selection and confirmed its suitability for our sun-tracking system.

It is also worth noting that the Corona DS-939MG Servo Motor allows us to avoid the need for an external power supply. A single servo draws approximately 200 mA, and since we do not intend to run both motors simultaneously, the total current remains within the 500 mA that the Arduino UNO can supply through its 5V pin — more details on this can be found here.

Coding

After assembly, we moved on to programming.

Naming the Photoresistor Pins

To work with the data, we needed to name the 4 photoresistors. We moved our device to the position where both servo motors are at 0 degrees — 0° for the horizontal and 0° for the vertical — and began naming the pins according to the photoresistors:

#define topLeft  A1
#define topRight A0
#define bottomLeft A3 
#define bottomRight A2 

Reading Sensor Data

Next, we needed to read data from the photoresistors. We created variables with short pin names, assigned the sensor readings to the corresponding variables, and printed them to the Serial Monitor:

#define topLeft  A1
#define topRight A0
#define bottomLeft A3 
#define bottomRight A2 

void setup() {
  Serial.begin(9600);
  pinMode(topLeft, INPUT);
  pinMode(topRight, INPUT);
  pinMode(bottomLeft, INPUT);
  pinMode(bottomRight, INPUT);
}

void loop() {
  int tL = analogRead(topLeft);
  int tR = analogRead(topRight);
  int bL = analogRead(bottomLeft);
  int bR = analogRead(bottomRight);

  Serial.print("tL:");
  Serial.println(tL);

  Serial.print("tR:");
  Serial.println(tR);

  Serial.print("bL:");
  Serial.println(bL);

  Serial.print("bR:");
  Serial.println(bR);
  delay(1000);
}

We also added delay(1000); so that sensor values are read once per second.

We then verified the photoresistors were connected correctly by covering each one with a finger.

(PHOTO)

Mapping Sensor Pairs to Servo Axes

The next step was to determine which pairs of photoresistors control the horizontal servo motor, and which control the vertical servo motor. The layout is as follows:

slide

Horizontal Servo Control

First, we developed code for the horizontal servo motor only.

We imported the Servo library:

#include <Servo.h>

Then created a servo object:

Servo horizontalservo;

And a global variable to track its position:

int horizontalpos = 90;

We initialized the variable with a value because the servo model we are using has no way of reporting its current position. For our task, we need to know the starting position of the servo — more on this shortly.

In the setup() function, we attached the servo to pin 8 and moved it to the initial position horizontalpos:

horizontalservo.attach(8);
horizontalservo.write(horizontalpos); 

We chose tL and tR as the comparison pair. Since a photoresistor works such that lower resistance corresponds to a lower analog reading, if tL < tR — meaning the Top Left sensor receives more light (and is oriented toward the 0° end of the servo’s range), while the Top Right sensor is oriented toward 180° — then we need to decrease horizontalpos by 1 degree.

Only 1 degree at a time, since after each movement we need to re-check whether the condition tL < tR still holds.

In code:

if(tL < tR) {
   horizontalpos = horizontalpos - 1;
   horizontalservo.write(horizontalpos);
   delay(100);
 } 

And the opposite condition:

if(tR < tL) {
   horizontalpos = horizontalpos + 1;
   horizontalservo.write(horizontalpos);
   delay(100);
 } 

Since we were working indoors, we tested the code using a flashlight.

(PHOTO)

Adding Boundary Limits

The code had one issue: servo position is bounded between [0, 180], but our code placed no limits on the value. So for the first condition, we added a check that horizontalpos > 0 — meaning the motor will only move further if its current position is above 0. If it has already reached 0, it stays there even if the light intensity on the tL side is higher than on tR.

We added the same constraint for the opposite direction:

if(tL < tR && horizontalpos > 0) {
   horizontalpos = horizontalpos - 1;
   horizontalservo.write(horizontalpos);
   delay(100);
 } 
if(tR < tL && horizontalpos < 180) {
   horizontalpos = horizontalpos + 1;
   horizontalservo.write(horizontalpos);
   delay(100);
 } 

Preventing Oscillation with a Threshold

This fixed the boundary issue, but there was still another problem. If both sensor values are close to each other — say 112 and 116 — the code treats that as a meaningful difference and rotates 1 degree. After rotating, the values might become 111 and 109, which triggers a rotation back, and so on indefinitely. The device would keep oscillating and never settle.

To prevent this, we added a third condition: the motor only moves if the percentage difference between the two sensors exceeds a configurable threshold, stored in a global variable:

int threshold = 5;

This value can be adjusted to tune the sensitivity of the device.

With this added, the relevant code became:

if(tL < tR && horizontalpos > 0 && (tR - tL)*100/tR > threshold) { // Added percentage threshold condition
   horizontalpos = horizontalpos - 1;
   horizontalservo.write(horizontalpos);
   delay(100);
 } 
if(tR < tL && horizontalpos < 180 && (tL - tR)*100/tL > threshold) { // Added percentage threshold condition
   horizontalpos = horizontalpos + 1;
   horizontalservo.write(horizontalpos);
   delay(100);
 } 

The device now worked correctly.

(VIDEO)

Refactoring: A Universal Function for Both Axes

The next step was to add the same logic for the vertical servo motor. Rather than duplicating the rotation logic, it made more sense to create a single universal function — which we did:

void sensdetect(int start, int finish, Servo &s, int &position ) {
  if(start < finish && position > 0 && (finish - start)*100/finish > threshold) {
    position = position - 1;
    s.write(position);
    delay(25);
  } 
  if(finish < start && position < 180 && (start - finish)*100/start > threshold) {
    position = position + 1;
    s.write(position);
    delay(25);
  } 
}

Where: - start — the sensor value corresponding to the 0° end of the servo’s range - finish — the sensor value corresponding to the 180° end - s — the servo motor (horizontal or vertical) - position — the current position of the corresponding servo

Final Code

With this single function handling both axes, the final code is as follows:

#include <Servo.h>

#define topLeft  A1
#define topRight A0
#define bottomLeft A3
#define bottomRight A2

int horizontalpos = 90;
int verticalpos = 90;
int threshold = 5;

Servo horizontalservo;
Servo verticalservo;

void setup() {
  Serial.begin(9600);
  pinMode(topLeft, INPUT);
  pinMode(topRight, INPUT);
  pinMode(bottomLeft, INPUT);
  pinMode(bottomRight, INPUT);
  horizontalservo.attach(8);
  verticalservo.attach(10);

  horizontalservo.write(horizontalpos); 
  delay(500);

  verticalservo.write(verticalpos); 
  delay(500);
}

void loop() {
  int tL = analogRead(topLeft);
  int tR = analogRead(topRight);
  int bL = analogRead(bottomLeft);
  int bR = analogRead(bottomRight);

  sensdetect(tL, tR, horizontalservo, horizontalpos);
  sensdetect(bL, tL, verticalservo, verticalpos);
}

void sensdetect(int start, int finish, Servo &s, int &position ) {
  if(start < finish && position > 0 && (finish - start)*100/finish > threshold) {
    position = position - 1;
    s.write(position);
    delay(25);
    Serial.print("position:");
    Serial.println(position);
  } 
  if(finish < start && position < 180 && (start - finish)*100/start > threshold) {
    position = position + 1;
    s.write(position);
    delay(25);
    Serial.print("position:");
    Serial.println(position);
  } 
}

Possible Improvements

The system is functional, but there is room for further development.

  1. The code works, but requires additional logic. For example, if the device’s head is at position (0°, 0°) and the light is most intense from the left side, there is currently no logic to make it rotate all the way to the opposite position (179°, 180°). The device would simply stop at the boundary and remain there. Additionally, the current code compares sensors in only two pairs: top-left vs. top-right to drive the horizontal servo, and bottom-left vs. top-left to drive the vertical servo. For more accurate tracking, comparisons between other sensor pairs should also be incorporated.

  2. It is also worth noting that our device locates the position of maximum light intensity, but it is not designed to actually rotate a solar panel. A real-world solar panel tracker would require a much more robust mechanical construction. Furthermore, for energy-efficient operation, the motor drive system should use worm gears. A worm gear prevents back-rotation when the motors are powered off — an important property for a tracker that needs to hold its position without continuously drawing power. A similar mechanism was used by our lab instructor Babken Chugaszyan in his final project.

  3. Additionally, placing capacitors (100–470 µF) across the power supply of each servo motor would help stabilize voltage, reducing noise and preventing unexpected resets of the microcontroller caused by current spikes during motor movement.


Last update: April 28, 2026