Photoresistor and Actuator Extension and Retraction Project

This project involves creating a photoresistor-based actuator control system using an Arduino. The system calibrates the baseline light level for 5 seconds upon startup. It continuously monitors light levels using a photoresistor, and when a significant change in light level (exceeding a predefined threshold) is detected, it triggers an actuator to extend for 4 seconds. After extending, the actuator enters a 2-minute cooldown period before retracting for another 4 seconds. The process then resets, ready to react to new changes in light levels. The control of the actuator is managed via relays. The issue I am having is that the actuator does not move once the photoresistor detects light change and the relays trigger in the sequence described above (e.g. relay 1 = extend actuator for 4 seconds when photoresistor detects light change, relay 2 = after 2 minutes retrack the actuator for 4 seconds). Using a multimeter I have examined readings between the different ports on the relays as the trigger and they seem to be changing the polarity across these as intended. I cannot figure out why the actuator will not move and need some guidance. Below I list the items used, a wiring description, and the code for the project. Any suggestions would be greatly appreciated.

Item List:

  1. Linear actuator: Linear Motion Actuators DC 12V 100mm Internal Limit Switch Low Noise Electric Linear Actuator for Industrial Agriculture(Stroke 100mm-15mm/s-50N)
  2. 2 channel relay: AEDIKO DC 5V Relay Module 2 Channel Relay Board with Optocoupler Support High or Low Level Trigger
  3. Photoresistor
  4. 12v battery
  5. Arduino board

Wiring:

  1. Power the Relay Module:
    o Connect the DC+ on the relay module to the 5V output on the Arduino board.
    o Connect the DC- on the relay module to the GND on the Arduino board.
  2. Connect the Relay Control Pins:
    o Connect IN1 on the relay module to the digital pin 6 on the Arduino board.
    o Connect IN2 on the relay module to the digital pin 7 on the Arduino board.
  3. Connect the Actuator to the Relays:
    o Connect COM1 on Relay 1 to the positive lead of the actuator.
    o Connect COM2 on Relay 2 to the negative lead of the actuator.
  4. Connect the Power Supply to the Relays:
    o Connect NO1 on Relay 1 to the positive terminal of the 12V power supply.
    o Connect NC1 on Relay 1 to the negative terminal of the 12V power supply.
    o Connect NO2 on Relay 2 to the negative terminal of the 12V power supply.
    o Connect NC2 on Relay 2 to the positive terminal of the 12V power supply.

IDE Code:
` // Define change threshold (adjust as needed based on readings)
const int changeThreshold = 50; // Adjust this based on photoresistor readings

// Variables to track the actuator state
bool actuatorExtended = false;

// Time in milliseconds to extend the actuator and cooldown period
const unsigned long extendTime = 4000; // 4 seconds
const unsigned long cooldownTime = 120000; // 2 minutes

// Variable to store the time when the relay was activated
unsigned long activationTime = 0;

// Variables for baseline light level calculation
long accumulatedLightLevel = 0;
int calibrationReadings = 0;
int baselineLightLevel = 0;

// Calibration time
const unsigned long calibrationTime = 5000; // 5 seconds
unsigned long startTime;

// Pin definitions
const int photoresistorPin = A0; // Adjust as needed
const int relay1 = 6; // Adjust as needed for IN1
const int relay2 = 7; // Adjust as needed for IN2

void setup() {
// Initialize serial communication
Serial.begin(9600);

// Set up the photoresistor pin
pinMode(photoresistorPin, INPUT);

// Set up the relay pins
pinMode(relay1, OUTPUT);
pinMode(relay2, OUTPUT);

// Ensure all relays are off at the start
digitalWrite(relay1, LOW); // Active HIGH, so LOW means off
digitalWrite(relay2, LOW); // Active HIGH, so LOW means off
Serial.println("Relays initialized to OFF state.");

// Initialize calibration
startTime = millis();
}

void loop() {
unsigned long currentTime = millis();

// Calibration phase
if (currentTime - startTime <= calibrationTime) {
// During calibration, accumulate the baseline light level readings
int reading = analogRead(photoresistorPin);
accumulatedLightLevel += reading;
calibrationReadings++;
Serial.print("Calibration reading: ");
Serial.println(reading);
return; // Skip the rest of the loop during calibration
} else if (baselineLightLevel == 0 && calibrationReadings > 0) {
// Calculate the average baseline light level after calibration
baselineLightLevel = accumulatedLightLevel / calibrationReadings;
Serial.print("Baseline Light Level: ");
Serial.println(baselineLightLevel);
}

// Read the photoresistor value
int sensorValue = analogRead(photoresistorPin);
int difference = sensorValue - baselineLightLevel;

// Print the sensor value and baseline to the serial monitor
Serial.print("Sensor Value: ");
Serial.println(sensorValue);
Serial.print("Baseline Light Level: ");
Serial.println(baselineLightLevel);
Serial.print("Difference: ");
Serial.println(difference);

// Additional debug information
Serial.print("Actuator state (extended): ");
Serial.println(actuatorExtended);

// Check for actuator extension
if (!actuatorExtended && abs(difference) >= changeThreshold) {
Serial.println("Condition met to extend actuator."); // Add this line
// Extend actuator
digitalWrite(relay1, HIGH); // Turn on relay 1 to extend actuator (active HIGH)
digitalWrite(relay2, LOW); // Ensure relay 2 is off
Serial.println("Relay 1 set to HIGH (ON), Relay 2 set to LOW (OFF)");
activationTime = currentTime; // Record the time the relay was activated
actuatorExtended = true; // Update the state to indicate actuator is extending
Serial.println("Actuator extending... Relay 1 ON, Relay 2 OFF");

// Wait for the actuator to extend
delay(extendTime);
digitalWrite(relay1, LOW); // Turn off the relay
Serial.println("Actuator fully extended. Relay 1 OFF.");

} else {
Serial.println("Condition not met to extend actuator.");
}

// Check if the cooldown period has ended and retract the actuator
if (actuatorExtended && currentTime - activationTime >= cooldownTime) {
Serial.println("Condition met to retract actuator."); // Add this line
// Retract actuator
digitalWrite(relay1, LOW); // Ensure relay 1 is off
digitalWrite(relay2, HIGH); // Turn on relay 2 to retract actuator (active HIGH)
Serial.println("Relay 2 set to HIGH (ON), Relay 1 set to LOW (OFF)");
Serial.println("Retracting actuator... Relay 2 ON, Relay 1 OFF");
delay(extendTime); // Wait for the actuator to retract
digitalWrite(relay2, LOW); // Turn off the retracting relay
actuatorExtended = false; // Reset the actuator state for next activation
Serial.println("Actuator retracted and ready for next cycle. Relay 2 OFF.");
} else if (actuatorExtended) {
Serial.print("Cooldown time remaining: ");
Serial.println(cooldownTime - (currentTime - activationTime));
}

// Small delay to avoid excessive sensor reading
delay(100);
}
`

You need to post your code properly so that it's readable.
In the IDE click on Edit, then Copy for Forum, that will copy your code. Then come here and just do a paste.

1 Like

Which one?

1 Like

Posting an annotated schematic showing exactly how you have wired it helps us help you. By showing all connections, power, components, ground and power sources you will save a lot of clarifying questions and time for all of us. Be sure to note any logic wires over 10/25 inches/cm in length. Post links to technical information on the hardware items including the motors, shield, and Arduino. This should include all component values or model numbers and details of all power supplies used (which could be USB power for example). Posting the code following forum guidelines using code tags will also help. With this information we should be able to answer your question accurately. "How to get the best from this forum".

I see, thank you for the insight. I followed your instructions and the code below is what the IDE provided.

	// Define change threshold (adjust as needed based on readings)
const int changeThreshold = 50; // Adjust this based on photoresistor readings

// Variables to track the actuator state
bool actuatorExtended = false;

// Time in milliseconds to extend the actuator and cooldown period
const unsigned long extendTime = 4000; // 4 seconds
const unsigned long cooldownTime = 120000; // 2 minutes

// Variable to store the time when the relay was activated
unsigned long activationTime = 0;

// Variables for baseline light level calculation
long accumulatedLightLevel = 0;
int calibrationReadings = 0;
int baselineLightLevel = 0;

// Calibration time
const unsigned long calibrationTime = 5000; // 5 seconds
unsigned long startTime;

// Pin definitions
const int photoresistorPin = A0; // Adjust as needed
const int relay1 = 6; // Adjust as needed for IN1
const int relay2 = 7; // Adjust as needed for IN2

void setup() {
  // Initialize serial communication
  Serial.begin(9600);

  // Set up the photoresistor pin
  pinMode(photoresistorPin, INPUT);

  // Set up the relay pins
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);

  // Ensure all relays are off at the start
  digitalWrite(relay1, LOW); // Active HIGH, so LOW means off
  digitalWrite(relay2, LOW); // Active HIGH, so LOW means off
  Serial.println("Relays initialized to OFF state.");

  // Initialize calibration
  startTime = millis();
}

void loop() {
  unsigned long currentTime = millis();

  // Calibration phase
  if (currentTime - startTime <= calibrationTime) {
    // During calibration, accumulate the baseline light level readings
    int reading = analogRead(photoresistorPin);
    accumulatedLightLevel += reading;
    calibrationReadings++;
    Serial.print("Calibration reading: ");
    Serial.println(reading);
    return; // Skip the rest of the loop during calibration
  } else if (baselineLightLevel == 0 && calibrationReadings > 0) {
    // Calculate the average baseline light level after calibration
    baselineLightLevel = accumulatedLightLevel / calibrationReadings;
    Serial.print("Baseline Light Level: ");
    Serial.println(baselineLightLevel);
  }

  // Read the photoresistor value
  int sensorValue = analogRead(photoresistorPin);
  int difference = sensorValue - baselineLightLevel;

  // Print the sensor value and baseline to the serial monitor
  Serial.print("Sensor Value: ");
  Serial.println(sensorValue);
  Serial.print("Baseline Light Level: ");
  Serial.println(baselineLightLevel);
  Serial.print("Difference: ");
  Serial.println(difference);

  // Additional debug information
  Serial.print("Actuator state (extended): ");
  Serial.println(actuatorExtended);

  // Check for actuator extension
  if (!actuatorExtended && abs(difference) >= changeThreshold) {
    Serial.println("Condition met to extend actuator."); // Add this line
    // Extend actuator
    digitalWrite(relay1, HIGH); // Turn on relay 1 to extend actuator (active HIGH)
    digitalWrite(relay2, LOW); // Ensure relay 2 is off
    Serial.println("Relay 1 set to HIGH (ON), Relay 2 set to LOW (OFF)");
    activationTime = currentTime; // Record the time the relay was activated
    actuatorExtended = true; // Update the state to indicate actuator is extending
    Serial.println("Actuator extending... Relay 1 ON, Relay 2 OFF");

    // Wait for the actuator to extend
    delay(extendTime);
    digitalWrite(relay1, LOW); // Turn off the relay
    Serial.println("Actuator fully extended. Relay 1 OFF.");
  } else {
    Serial.println("Condition not met to extend actuator.");
  }

  // Check if the cooldown period has ended and retract the actuator
  if (actuatorExtended && currentTime - activationTime >= cooldownTime) {
    Serial.println("Condition met to retract actuator."); // Add this line
    // Retract actuator
    digitalWrite(relay1, LOW);  // Ensure relay 1 is off
    digitalWrite(relay2, HIGH); // Turn on relay 2 to retract actuator (active HIGH)
    Serial.println("Relay 2 set to HIGH (ON), Relay 1 set to LOW (OFF)");
    Serial.println("Retracting actuator... Relay 2 ON, Relay 1 OFF");
    delay(extendTime); // Wait for the actuator to retract
    digitalWrite(relay2, LOW); // Turn off the retracting relay
    actuatorExtended = false; // Reset the actuator state for next activation
    Serial.println("Actuator retracted and ready for next cycle. Relay 2 OFF.");
  } else if (actuatorExtended) {
    Serial.print("Cooldown time remaining: ");
    Serial.println(cooldownTime - (currentTime - activationTime));
  }

  // Small delay to avoid excessive sensor reading
  delay(100);
}



Arduino Uno!

Can your 12V battery supply enough current to the actuator?
Are you performing these tests without a load on the actuator?
Have you tested the actuator by connecting it directly to the battery?

This is how you have the relays wired.
When the program starts you turn both relays OFF so both are NC and the actuator will move or stall depend on the +/- actuator connection.
Your program should set relay 1 OFF and relay 2 ON when it starts.

1 Like

Yes, i tested for this and the battery supplies enough current.

Okay, Just so i am clear. I need to change the code to have the initial state of the relays be such that relay 1 is off and relay 2 is on? Instead of what i currently have programmed, which is that they are both initially off. Is that correct?

If the schematic I show in post #8 matches your word description of the connections then yes. That would connect both wires of the actuator to -12V.
To move the actuator both relays need to be OFF or both ON

Actually I would rewire it so that relay2 NC is connected to -12 and NO connected to +12, same as relay 1
Then your code can set both relays OFF at the beginning and then to activate the actuator, one relay is ON and the other OFF

You sure? On my relay boards, Arduino GND is not connected.

6m95zlV

Here's a test sketch to take your actuator for a rip in any case. Type chars in Serial monitor to control. The magic chest in my profile photo uses this to open and close.

char rxChar;
boolean newData = false;
char trashChar = ';';

const int forwards = 7;   // pin for motor shield
const int backwards = 6;  // pin for motor shield
const int readyLed = LED_BUILTIN;

void setup() {
  pinMode(forwards, OUTPUT);   //set relay as an output
  pinMode(backwards, OUTPUT);  //set relay as an output
  pinMode(readyLed, OUTPUT);
  digitalWrite(readyLed, LOW);
  Serial.begin(115200);
}

void loop() {
  recvOneChar();
  showNewData();
  switch (rxChar) {

    /************  full open-hold(some duration)-close functions  ******/
    case ('b'):
      normalLong();
      break;
    case ('o'):
      normal();
      break;
    case ('p'):
      halfShort();
      break;
    case ('u'):
      halfMilli();
      break;
    case ('v'):
      quarterShort();
      break;
    case ('w'):
      quarterMilli();
      break;
    case ('x'):
      eightShort();
      break;
    case ('y'):
      eightTiny();
      break;
    case ('z'):
      eightTease();
      break;
    /************ end full functions  ***********************************/

    /************ core functions/discrete operations ********************/
    case ('s'):
      stopAct();
      break;
    case ('h'):
      holdAct();
      break;
    case ('f'):
      fullExtend();
    case ('e'):
      extend();
      break;
    case ('a'):
      halfE();
      break;
    case ('t'):
      quarterE();
      break;
    case ('c'):
      fullRetract();
      break;
    case ('r'):
      retract();
      break;
    case ('l'):
      halfR();
    case ('d'):
      quarterR();
      break;
    case ('k'):
      holdLong();
      break;
  }
  /******************** end discrete operations  ************************/
  /******************** end switch/case *********************************/
  digitalWrite(readyLed, LOW);
  rxChar = trashChar;
}
/******************** end void loop(){} *******************************/
void recvOneChar() {
  if (Serial.available() > 0) {
    rxChar = Serial.read();
    newData = true;
  }
}

void showNewData() {
  if (newData == true) {
    newData = false;
  }
}

/**************************   COMBINED FUNCTIONS  *********************/
void normalLong() {  // 1 minute
  extend();
  holdLong();
  retract();
}

void normal() {  // hold 30 sec
  extend();
  holdAct();
  retract();
}

void halfShort() {  // hold 3 sec
  halfE();
  stopAct();
  halfR();
}

void halfMilli() {  // hold 1.6
  halfE();
  milliStop();
  halfR();
}

void quarterShort() {  // hold 3
  quarterE();
  stopAct();
  quarterR();
}

void quarterMilli() {  // hold 1.6
  quarterE();
  milliStop();
  quarterR();
}

void eightShort() {  // hold 3
  eightOpen();
  stopAct();
  eightClose();
}

void eightTiny() {  // hold 1.6
  eightOpen();
  milliStop();
  eightClose();
}

void eightTease() {  // hold 0.8
  eightOpen();
  microStop();
  eightClose();
}

/**************************   DISCRETE FUNCTIONS   *******************************
      Activate the relay one direction, they must be different to move the motor
              this particular actuator takes 20 seconds each way */

/*************************   OPENING FUNCTIONS  *********************************/
void fullExtend() {
  digitalWrite(backwards, HIGH);
  digitalWrite(forwards, LOW);
  digitalWrite(readyLed, HIGH);
  delay(22000);
}
void extend() {
  digitalWrite(backwards, HIGH);
  digitalWrite(forwards, LOW);
  digitalWrite(readyLed, HIGH);
  delay(20000);
}
void halfE() {
  digitalWrite(backwards, HIGH);
  digitalWrite(forwards, LOW);
  digitalWrite(readyLed, HIGH);
  delay(10000);
}
void quarterE() {
  digitalWrite(backwards, HIGH);
  digitalWrite(forwards, LOW);
  digitalWrite(readyLed, HIGH);
  delay(5000);
}
void eightOpen() {
  digitalWrite(backwards, HIGH);
  digitalWrite(forwards, LOW);
  digitalWrite(readyLed, HIGH);
  delay(2500);
}


/**************************    CLOSING FUNCTIONS  *************************************/
void fullRetract() {
  digitalWrite(backwards, LOW);
  digitalWrite(forwards, HIGH);
  digitalWrite(readyLed, HIGH);
  delay(22000);
}
void retract() {
  digitalWrite(backwards, LOW);
  digitalWrite(forwards, HIGH);
  digitalWrite(readyLed, HIGH);
  delay(20000);
}
void halfR() {
  digitalWrite(backwards, LOW);
  digitalWrite(forwards, HIGH);
  digitalWrite(readyLed, HIGH);
  delay(10500);
}
void quarterR() {
  digitalWrite(backwards, LOW);
  digitalWrite(forwards, HIGH);
  digitalWrite(readyLed, HIGH);
  delay(5500);
}
void eightClose() {
  digitalWrite(backwards, LOW);
  digitalWrite(forwards, HIGH);
  digitalWrite(readyLed, HIGH);
  delay(2500);
}

/********************************   STOP/HOLD FUNCTIONS  ********************************/
void stopAct() {
  digitalWrite(forwards, HIGH);
  digitalWrite(backwards, HIGH);  //Deactivate both relays to brake the motor
  digitalWrite(readyLed, HIGH);
  delay(3000);
}
void milliStop() {
  digitalWrite(forwards, HIGH);
  digitalWrite(backwards, HIGH);  //Deactivate both relays to brake the motor
  digitalWrite(readyLed, HIGH);
  delay(1600);
}
void microStop() {
  digitalWrite(forwards, HIGH);
  digitalWrite(backwards, HIGH);  //Deactivate both relays to brake the motor
  digitalWrite(readyLed, HIGH);
  delay(800);
}
void holdAct() {
  digitalWrite(forwards, HIGH);
  digitalWrite(backwards, HIGH);  //Deactivate both relays to brake the motor
  digitalWrite(readyLed, HIGH);
  delay(30000);
}
void holdLong() {
  digitalWrite(forwards, HIGH);
  digitalWrite(backwards, HIGH);  //Deactivate both relays to brake the motor
  digitalWrite(readyLed, HIGH);
  delay(60000);
}

(Yep, it uses the dreaded delay() throughout. I wrote it how I wrote it. )

  • connect gnd from arduino to relay module ( you need a common gnd )
  • disconnect arduino 5V from relay module vcc ( or you'll short the two power supply )

I implemented your suggestions and they worked! Thank you, and everyone who commented, for your willingness to help! It is greatly appreciated!

You are welcome
Have a nice day!