Servo and Motor wont run at the same time

Hey guys, im trying to program a servo to turn 90 degrees CCW and 90 degrees CW automatically and simultaneously program a motor (through an H-bridge) to automatically run unless it measures a variable proportional to the voltage below 100 from input pin 2 which is connected to a phototransistor (meant to measure the brightness of the environment) on a DFRobot FireBeetle2 ESP32-C6. Unfortunately, when I ran this code:

long sum = 0;
Servo s1;
const uint8_t LDR = 2; 
const uint8_t Motor_1 = 4;
const uint8_t Motor_2 = 7;

void setup() {
  Serial.begin(9600);
  pinMode(LDR, INPUT);
  pinMode(Motor_1, OUTPUT);
  pinMode(Motor_2, OUTPUT);
  s1.attach(5);
  
  s1.write(90);  // Start at the center position
  delay(500);    // Allow servo to initialize
}

void loop() {

   int voltage_read= (analogRead(LDR)*0.25);
    digitalWrite(Motor_1, HIGH);
    digitalWrite(Motor_2, LOW);

  Serial.println(voltage_read); 
  sum=0;
  
  for (int i = 0; i < 8; i++) {
    sum += voltage_read; 
    delay(250); // Read value from each input and accumulate the sum
  }
  
  long average = sum / 8;
   if(average < 100 ) {
    Serial.print("Too dark!");
    digitalWrite(Motor_1, LOW);
    digitalWrite(Motor_2, LOW);
   }

 static int position = 90;  // Start at center
  static int direction = 1;  // 1 = right, -1 = left
  
  position += direction * 2; // Move 2 degrees per step

  // Change direction at limits (110° and 70°)
  if (position >= 190) {
    direction = -1; 
     // Change direction to left
  } else if (position <= 60) {
    delay(25);
    direction = 1;   // Change direction to right
  }

  s1.write(position);  // Move servo
  delay(25);  // Smooth movement
}

the motor system operated as predicted but the servo didn't operate. I made a new sketch and tested only the servo program:

#include <ESP32Servo.h>
Servo s1;
void setup() {
  // put your setup code here, to run once:
  s1.attach(5);
    s1.write(90);  // Start at the center position
  delay(500);    // Allow servo to initialize
}

void loop() {
  // put your main code here, to run repeatedly:
 static int position = 90;  // Start at center
  static int direction = 1;  // 1 = right, -1 = left
  
  position += direction * 2; // Move 2 degrees per step

  // Change direction at limits (110° and 70°)
  if (position >= 190) {
    direction = -1; 
     // Change direction to left
  } else if (position <= 60) {
    delay(25);
    direction = 1;   // Change direction to right
  }

  s1.write(position);  // Move servo
  delay(25);  // Smooth movement
}

and it successfully operated, indicating that the code, I believe, is not the issue. I began looking at my circuit, specifically, when I isolated the motor aspect, which I thought drained too much current, especially since it was in parallel, I found the servo still didn't drive. For further context, Ive tried with my laptop as VCC and an external battery holder that supplied 9V for the motor and a voltage regulator to lower that to 5V for the firebeetle and the rest of my circuit to handle and I measured the voltage with a multimeter to ensure my motor and servo circuit were getting 8V and 5V respectively. In case you guys would like to view my circuit, here's a picture I made of it below:

Your first sketch does not compile. No library included.

How does this read inputs? It looks like it reads one LDR and doubles it (but why reading * .25 * 8?)... and why delay() during a multiplication?

I moved your topic to a more appropriate forum category @t-dog-69.

The Nano Family > Nano ESP32 category you chose is only used for discussions directly related to the Arduino Nano ESP32 board.

In the future, when creating a topic please take the time to pick the forum category that best suits the subject of your question. There is an "About the _____ category" topic at the top of each category that explains its purpose.

Thanks in advance for your cooperation.

I can't get the meaning of life... huh... of this code. :wink:

  int voltage_read= (analogRead(LDR)*0.25);
  digitalWrite(Motor_1, HIGH);
  digitalWrite(Motor_2, LOW);

  Serial.println(voltage_read); 
  sum=0;
  
  for (int i = 0; i < 8; i++) {
    sum += voltage_read; 
    delay(250); // Read value from each input and accumulate the sum
  }
  
  long average = sum / 8;

Here you read the LDR value once, mulptiply by 0.25 meaning you divide it by 4, so the range goes from 0 to 255 (single byte). I don't know why you need to lower the resolution, anyway...
Then you cycle 8 times adding the same first value, so you will double it, ranging from 0 to 2047 (!). And that cycle with a delay(250) requires 250*8=2 seconds to get the sum, while doing nothing! And lastly, you divide it by 8 to get the same first 0-255 range.
Why?

If you need to get an average of 8 consecutive readings you should read the analog value 8 times (with shorter delays):

  digitalWrite(Motor_1, HIGH);
  digitalWrite(Motor_2, LOW);
  sum=0;
  for (int i = 0; i < 8; i++) {
    sum += analogRead(LDR); 
    delay(5);
  }
  int average = sum / 8;

The following code has also something to be checked, like at least one unnecessary delay, and a condition to detect whan the direction must change.

And, as a last general tip, make the code cleaner with a standard indentation (use IDE Ctrl-T command to have it automatically before posting here) and some more comments (especially when posting here, to let us quickly understand wht you meant to do), use all uppercase names for constants and #define symbols (it's not mandatory but helps distinguish variables from constants), and for pin number definitions I use a "P_" prefix.

But I'm not entirely convinced by your engine management algorithm, I see you set motor 1 to HIGH and Motor 2 to LOW on each program loop, and stop both when it's too dark: when does the Motor 2 run?

Aside from that for now, if I were you I'd try something like this (it's untested, but you can see the changes I made and how it's a bit more easily readable):

// Use symbols at the top to make things clearer and easier to customize
#define MOVE_RIGHT 1
#define MOVE_LEFT -1
#define RIGHT_POS 190
#define LEFT_POS 60
#define LOW_LIGHT 100
// Pins
#define P_LDR 2 
#define P_MOTOR_1 4
#define P_MOTOR_2 7

// moved to global (unless required there's no advantage in using statics)
int position = 90;  // Start at center
int direction = MOVE_RIGHT;

long sum = 0;
Servo s1;

void setup() {
  Serial.begin(9600);
  pinMode(P_LDR, INPUT);
  pinMode(P_MOTOR_1, OUTPUT);
  pinMode(P_MOTOR_2, OUTPUT);
  s1.attach(5);
  
  s1.write(position);
  delay(500);    // Allow servo to initialize
}

void loop() {
  // Ok, but are you sure?? When does Motor 2 run?
  digitalWrite(P_MOTOR_1, HIGH);
  digitalWrite(P_MOTOR_2, LOW);
  // Read average
  sum=0;
  for (int i = 0; i < 8; i++) {
    sum += analogRead(LDR); 
    delay(5);
  }
  int average = sum / 8;
  // If too dark, stop the motors
  if(average < LOW_LIGHT) {
    Serial.print("Too dark!");
    digitalWrite(P_MOTOR_1, LOW);
    digitalWrite(P_MOTOR_2, LOW);
  }
  
  position += direction * 2; // Move 2 degrees per step
  // Change direction at limits (110° and 70°)
  if (direction == MOVE_RIGHT && position > RIGHT_POS) {
    direction = MOVE_LEFT; 
  } else if (direction == MOVE_LEFT && position < LEFT_POS) {
    direction = MOVE_RIGHT;
  }
  s1.write(position);  // Move servo
  delay(25);  // Smooth movement
}

My apologies, when I copied and pasted the code from Arduino IDE, I forgot the library, the library is ESP32Servo.h and it was included. For context, this device is going on a small car to drive it and the motor needs to adapt to the color of the floor and to ensure it reads accurate readings, I added the for loop where I average the value of 8 readings over a short time interval to ensure I don't act on glares or bad lighting in specific areas. The delay is there to make sure I take readings over a time interval and not all at once or over too quick of a time interval. The multiply by 0.25 was when I planned on doing analogwrite with the motor (which operates on a scale of 255) but I got rid of that and I figured that small resolution advantage I would have if I change wouldn't be worth it since I'm used to readings out of a scale of 255.

1 Like

The lower resolution was made when I was doing analog write with the motor but I quickly got rid of that and figured not to change it since I'm already used to a scale of 255 for my readings. Your right, I should've added comments, but I forgot to. the Motor_1 is the pin connecting to one input of the H-bridge and the other is connected to the other input. Anyhow, I will make adjustments to the code, but do you think there's an error that causes my motor and my servo not to run at the same time? Because the servo code works and so does the motor (although it could use some adjustments as you mentioned) but they won't work simultaneously? This is what I'm more concerned about.

EDIT: I ran your code docdoc and it worked successfully with the servo although I don't have the motor on me right now to test it (somebody else has it) but considering the past code worked I'm sure this will too. However, for future reference, other than the time inefficiencies your code resolved (too many delays I had), I'm not sure why mine didn't work and yours did? Because to me they do the same thing overall? Perhaps did the delays from mine add up to make a difference in the servo?

Still unclear to me your motor algorithm: what has the motor to do with the average (and with the 255 limit)? Nothing in your code shows anything like that, you just get the average value from LDR, and if lower than the limit (you can adjust it even if you have it on 0-1023 range) shut the motor down.
By the way I would also recommend you to modify that part like this (if you need to activate the engine only that way anyway):

...
  // Read average
  sum=0;
  for (int i = 0; i < 8; i++) {
    sum += analogRead(LDR); 
    delay(5);
  }
  int average = sum / 8;
  // If too dark, stop the motors
  if(average < LOW_LIGHT) {
    // Stop motor
    Serial.print("Too dark!");
    digitalWrite(P_MOTOR_1, LOW);
    digitalWrite(P_MOTOR_2, LOW);
  } else {
    // Keep motor running (always the same direction?)
    digitalWrite(P_MOTOR_1, HIGH);
    digitalWrite(P_MOTOR_2, LOW);
  }
...

Well, your code made a useless 2-seconds delay while trying to get the average (also in a wrong way, with a single reading not 8): Arduino isn't multitasking, so that means the code does nothing to motor and servo for this 2 seconds delay, and it happens on each loop.
It's always a bad idea using delays unless you know the code doesn't need to do anything else (aka: the system is unresponsive or is performing a single task).
Generally the best way to manage timed things is using millis() function, unless you really want to do single things, and in a fixed sequence. If you need to control a motor and a servo, you have two.

You need something more substantial then the (assumed) 9V smoke alarm battery. It will not do the job.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.