[SLOVED]Regarding the issue of randomizing the animatronic eye movements

Hello, I need to work on a project similar to this one: https://www.instructables.com/Simplified-3D-Printed-Animatronic-Dual-Eye-Mechani/, but instead of joystick control, I want it to move randomly. However, I’ve run into a major problem—the program doesn’t work when it reaches void loop. I hope someone skilled can help me solve this issue. Thank you for your assistance!

Using ESP-32S and PWM PCA9685 and SG90 servo

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

// Adjust the pulse range for SG90 servos
#define SERVOMIN  (500)   // Minimum pulse for SG90 (corresponding to 0 degrees)
#define SERVOMAX  (2500)  // Maximum pulse for SG90 (corresponding to 180 degrees)

#define ANGLE_RANGE 60  // Set maximum movement range to 60 degrees
#define CENTER_ANGLE 90 // Set center angle to 90 degrees

int servo0 = 0; // Left-right movement
int servo1 = 1; // Up-down movement
int servo2 = 2; // Left upper eyelid blink
int servo3 = 3; // Right upper eyelid blink
int servo4 = 4; // Left lower eyelid blink
int servo5 = 5; // Right lower eyelid blink

void setup() {
  Serial.begin(9600);
  pwm.begin();
  pwm.setPWMFreq(60);  // Set PWM frequency to 60 Hz

  // Calibrate servos to minimum and maximum angles
  calibrateServos();
}

void setServoPulse(uint8_t n, double pulse) {
  double pulselength = 1000000 / 60 / 4096;  // 60Hz, 12-bit resolution
  pulse = pulse * 1000 / pulselength;        // Convert to pulse width
  pwm.setPWM(n, 0, pulse);
}

void calibrateServos() {
  Serial.println("Calibrating servos...");

  // Calibrate servos to the minimum position
  for (int i = 0; i < 6; i++) {
    pwm.setPWM(i, 0, SERVOMIN);
  }
  delay(300); // Shorten delay to reduce calibration time

  // Calibrate servos to the maximum position
  for (int i = 0; i < 6; i++) {
    pwm.setPWM(i, 0, SERVOMAX);
  }
  delay(300); // Shorten delay to reduce calibration time

  // After calibration, return to the center position
  for (int i = 0; i < 6; i++) {
    pwm.setPWM(i, 0, (SERVOMIN + SERVOMAX) / 2);
  }
  delay(300); // Shorten delay to reduce calibration time

  Serial.println("Calibration complete.");
}

void loop() {
  // Display message and start random movements
  Serial.println("Starting random movements...");
  lookLeftAndRight();
  lookUpAndDown();
  blinking();
}

void lookLeftAndRight() {
  Serial.println("Moving left and right...");
  // Set movement range to 60 degrees
  int leftLimit = (SERVOMIN + SERVOMAX) / 2 - (ANGLE_RANGE * 11); // Left range
  int rightLimit = (SERVOMIN + SERVOMAX) / 2 + (ANGLE_RANGE * 11); // Right range

  for (uint16_t pulselen = leftLimit; pulselen < rightLimit; pulselen += random(3, 6)) {
    pwm.setPWM(servo0, 0, pulselen); // Set servo pulse width
    delay(random(10, 30)); // Random delay
  }
  for (uint16_t pulselen = rightLimit; pulselen > leftLimit; pulselen -= random(3, 6)) {
    pwm.setPWM(servo0, 0, pulselen); // Set servo pulse width
    delay(random(10, 30)); // Random delay
  }
}

void lookUpAndDown() {
  Serial.println("Moving up and down...");
  // Set up and down movement range to 60 degrees
  int upLimit = (SERVOMIN + SERVOMAX) / 2 - (ANGLE_RANGE * 11); // Upper range
  int downLimit = (SERVOMIN + SERVOMAX) / 2 + (ANGLE_RANGE * 11); // Lower range

  for (uint16_t pulselen = upLimit; pulselen < downLimit; pulselen++) {
    pwm.setPWM(servo1, 0, pulselen); // Set servo pulse width
    delay(random(10, 30)); // Random delay
  }
  for (uint16_t pulselen = downLimit; pulselen > upLimit; pulselen--) {
    pwm.setPWM(servo1, 0, pulselen); // Set servo pulse width
    delay(random(10, 30)); // Random delay
  }
}

void blinking() {
  Serial.println("Blinking eyes...");
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(servo2, 0, pulselen); // Left upper eyelid blink
    pwm.setPWM(servo3, 0, pulselen); // Right upper eyelid blink
    pwm.setPWM(servo4, 0, pulselen); // Left lower eyelid blink
    pwm.setPWM(servo5, 0, pulselen); // Right lower eyelid blink
    delay(8); // Reduce delay to increase blink speed
  }
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    pwm.setPWM(servo2, 0, pulselen); // Left upper eyelid blink
    pwm.setPWM(servo3, 0, pulselen); // Right upper eyelid blink
    pwm.setPWM(servo4, 0, pulselen); // Left lower eyelid blink
    pwm.setPWM(servo5, 0, pulselen); // Right lower eyelid blink
    delay(8); // Reduce delay to increase blink speed
  }
}

Welcome to the forum

That is a very lame description of your problem

What should the sketch do and what does it actually do ?
For instance, do you see any Serial output ?

My wiring from the ESP32S to the PCA9680 is as follows:

  1. VCC (left side): Connected to the 3.3V pin on the ESP32.

  2. GND (left side): Connected to the GND pin on the ESP32.

  3. SCL: Connected to the I2C SCL pin on the ESP32 (GPIO 22).

  4. SDA: Connected to the I2C SDA pin on the ESP32 (GPIO 21).

I believe the wiring for the other servo motors is correct.

Additionally, my program is primarily designed to control a pair of eyes that randomly move left and right, randomly move up and down, and blink randomly.

Servo0 controls the left/right eye movement.

Servo1 controls the up/down eye movement.

Servo2 controls the left upper eyelid for blinking.

Servo3 controls the right upper eyelid for blinking.

Servo4 controls the left lower eyelid for blinking.

Servo5 controls the right lower eyelid for blinking.

However, in practice, after uploading the code, the serial output shows that it performs the actions above, but very slowly—it takes about 10–20 seconds to show the next action. The servos themselves don't move at all; occasionally, the servo controlling the up/down movement twitches, and after some time, all the servos stop responding entirely.

Thank you for helping :smiling_face_with_three_hearts:


Serial output display like this ,but servo doesn't work😢

Does the servo example that came with the servo library work with your hardware setup ?

I haven't tested the servo library's example program, but I have run test programs on my servos and PCA9680, and they all work.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

#define SERVOMIN  (500)  // Minimum pulse width for SG90
#define SERVOMAX  (2500) // Maximum pulse width for SG90

int servo0 = 0; // Left/right eye movement
int servo1 = 1; // Up/down eye movement
int servo2 = 2; // Left upper eyelid
int servo3 = 3; // Right upper eyelid
int servo4 = 4; // Left lower eyelid
int servo5 = 5; // Right lower eyelid

void setup() {
  Serial.begin(9600);
  pwm.begin();
  pwm.setPWMFreq(60);  // Set PWM frequency to 60 Hz

  // Test Servo 0 (left/right eye movement)
  Serial.println("Testing servo0 (left/right)...");
  pwm.setPWM(servo0, 0, SERVOMIN); // Set to left
  delay(1000); // Wait for 1 second
  pwm.setPWM(servo0, 0, SERVOMAX); // Set to right
  delay(1000); // Wait for 1 second

  // Test Servo 1 (up/down eye movement)
  Serial.println("Testing servo1 (up/down)...");
  pwm.setPWM(servo1, 0, SERVOMIN); // Set to down
  delay(1000); // Wait for 1 second
  pwm.setPWM(servo1, 0, SERVOMAX); // Set to up
  delay(1000); // Wait for 1 second

  // Test left upper eyelid (servo2)
  Serial.println("Testing left upper eyelid (servo2)...");
  pwm.setPWM(servo2, 0, SERVOMIN); // Start blink
  delay(1000); // Wait for 1 second
  pwm.setPWM(servo2, 0, SERVOMAX); // Stop blink
  delay(1000); // Wait for 1 second

  // Test right upper eyelid (servo3)
  Serial.println("Testing right upper eyelid (servo3)...");
  pwm.setPWM(servo3, 0, SERVOMIN); // Start blink
  delay(1000); // Wait for 1 second
  pwm.setPWM(servo3, 0, SERVOMAX); // Stop blink
  delay(1000); // Wait for 1 second

  // Test left lower eyelid (servo4)
  Serial.println("Testing left lower eyelid (servo4)...");
  pwm.setPWM(servo4, 0, SERVOMIN); // Start blink
  delay(1000); // Wait for 1 second
  pwm.setPWM(servo4, 0, SERVOMAX); // Stop blink
  delay(1000); // Wait for 1 second

  // Test right lower eyelid (servo5)
  Serial.println("Testing right lower eyelid (servo5)...");
  pwm.setPWM(servo5, 0, SERVOMIN); // Start blink
  delay(1000); // Wait for 1 second
  pwm.setPWM(servo5, 0, SERVOMAX); // Stop blink
  delay(1000); // Wait for 1 second
}

void loop() {
  // After testing, the program will stop here.
 
}


get following when i add prints

07:09:12.521 -> Calibrating servos...
07:09:13.412 -> Calibration complete.
07:09:13.412 -> Starting random movements...
07:09:13.457 -> Moving left and right...
07:09:13.506 ->  pulselen 840
07:09:13.506 ->  pulselen 844
07:09:13.551 ->  pulselen 849
07:09:13.551 ->  pulselen 854
07:09:13.551 ->  pulselen 859
07:09:13.551 ->  pulselen 863
07:09:13.599 ->  pulselen 868
07:09:13.599 ->  pulselen 873
...
07:09:19.962 ->  pulselen 2114
07:09:19.962 ->  pulselen 2117
07:09:20.010 ->  pulselen 2121
07:09:20.057 ->  pulselen 2126
07:09:20.057 ->  pulselen 2130
07:09:20.057 ->  pulselen 2135
07:09:20.057 ->  pulselen 2138
07:09:20.105 ->  pulselen 2142
07:09:20.151 ->  pulselen 2147
07:09:20.151 ->  pulselen 2151
07:09:20.198 ->  pulselen 2155
07:09:20.198 ->   next loop
07:09:20.198 ->  pulselen 2160
07:09:20.198 ->  pulselen 2155
07:09:20.198 ->  pulselen 2151
07:09:20.244 ->  pulselen 2146
07:09:20.244 ->  pulselen 2142
07:09:20.291 ->  pulselen 2138
07:09:20.291 ->  pulselen 2135
07:09:20.338 ->  pulselen 2132
07:09:20.384 ->  pulselen 2127
07:09:20.384 ->  pulselen 2122
07:09:20.431 ->  pulselen 2117
...
07:09:26.905 ->  pulselen 862
07:09:26.952 ->  pulselen 859
07:09:26.952 ->  pulselen 856
07:09:26.999 ->  pulselen 853
07:09:26.999 ->  pulselen 850
07:09:27.046 ->  pulselen 847
07:09:27.046 ->  pulselen 843
07:09:27.046 -> Moving up and down...


So do I,when I tested it, it also didn’t enter the loop. How should I solve this problem? Thank you.:smiling_face_with_three_hearts:

what does "didn't enter the loop" or "doesn't work when it reaches void loop" mean?

you don't see Moving left and right...

I'm very sorry. After starting the program, it performs calibration in the setup function, but when it enters the loop, it gets stuck. The servo also doesn't move. The serial output shows the left-right movement, but the subsequent actions appear very slowly in the serial monitor, and the servo still doesn't respond.

seems that you need to write a small program to verify your understanding of how to use the adaFruit library to control a servo

in a model RR application using the PCA9685 SERVOMAX/MIN are 126/625, you set them to 500/2500

Got it, thanks for the advice, I will try it tomorrow . If I have any questions, I will reach out to you for help.:grinning:

The BYTE magazine of modeling.

Hello everyone, I have completed my program. Initially, I wrote int as uint, which caused my servo to be unable to go into negative values and prevented it from proceeding to the next step in the loop. After modifying the program, I successfully got it to work. Next, I wrote a switch case program in the loop to allow it to pick a case and create random variations. Thank you all for your help, which has helped me improve a lot. Here is my program:

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

#define SERVOMIN (250)
#define SERVOMAX (400)
#define SERVOCENTER (325)

int servo0 = 0; // Left-right movement
int servo1 = 1; // Up-down movement
int servo2 = 2; // Left upper eyelid
int servo3 = 3; // Right upper eyelid
int servo4 = 4; // Left lower eyelid
int servo5 = 5; // Right lower eyelid

void setup() {
  Serial.begin(9600);
  pwm.begin();
  pwm.setPWMFreq(60);
}

void setServoPulse(uint8_t n, double pulse) {
  double pulselength = 1000000 / 60 / 4096;
  pulse = pulse * 1000 / pulselength;
  pwm.setPWM(n, 0, pulse);
}

void loop() {
  
  int action = random(1, 13); // Randomly select an action
  Serial.println(action);
  switch (action) {
    case 1:
      randomMovement();
      break;
    case 2:
      blinkEyes() ;

      break;
    case 3:
      blinkEyes() ;
      delay(200) ;
      blinkEyes() ;
    
      delay(random(1,10)) ;

      break;
    case 4:
      delay(random(100,1000)) ;

      break;
    case 5:
      blinkEyes() ;
      closeEyes() ;
      delay(random(50,500));
      blinkEyes();

      break;
    case 6:
      randomMovement() ;
      delay(random(100,500)) ; 
      randomMovement() ;
      break;
      case 7:
        randomMovement() ;
        delay(random(100,500)) ; 
        blinkEyes() ;
        break;
      case 8:
        randomMovement() ;
        delay(random(100,500)) ; 
        blinkEyes() ;
        delay(random(10,100)) ;
        randomMovement() ;
        break;
      case 9:
      delay(random(1000,2000));
      blinkEyes();
      delay(random(300,500));
      closeEyes();
      delay(random(100,300));
      blinkEyes();
      break;
      case 10:
      randomMovement();
      delay(random(300,1000));
      randomMovement();
      delay(random(300,1000));
      randomMovement();
      break;
      case 11:
      randomMovement();
      delay(random(300,1000));

      randomMovement();
      delay(random(300,1000));
      randomMovement();
      blinkEyes();
      break;
      case 12:
      delay(random(500,2000));
      break;
  }
}

// Random left-right movement
void randomMovement() {
  Serial.println("EyesLR");
  // Randomly move left and right
  if (random(2)) {
    for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen += random(1, 5)) {
      pwm.setPWM(servo0, 0, pulselen);
      delay(random(3, 10));
    }
  } else {
    for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen -= random(1, 5)) {
      pwm.setPWM(servo0, 0, pulselen);
      delay(random(3, 10));
    }
  }
  delay(random(1,10));
}

void blinkEyes() {
  Serial.println("EyesBlinkingUP");
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(servo4, 0, pulselen); // Move the left upper eyelid forward
    pwm.setPWM(servo5, 0, SERVOMAX - pulselen + SERVOMIN); // Move the right upper eyelid backward
    pwm.setPWM(servo2, 0, SERVOMAX - pulselen + SERVOMIN); // Move the left lower eyelid backward
    pwm.setPWM(servo3, 0, pulselen); // Move the right lower eyelid forward
    delay(2);
  }
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    pwm.setPWM(servo4, 0, pulselen);
    pwm.setPWM(servo5, 0, SERVOMAX - pulselen + SERVOMIN);
    pwm.setPWM(servo2, 0, SERVOMAX - pulselen + SERVOMIN);
    pwm.setPWM(servo3, 0, pulselen);
    delay(2);
  }
  delay(random(1,10));
}

void closeEyes() {
  Serial.println("EyesBlinkingDOWN");
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(servo4, 0, SERVOMAX - pulselen + SERVOMIN); // Move the left upper eyelid backward
    pwm.setPWM(servo5, 0, pulselen); // Move the right upper eyelid forward
    pwm.setPWM(servo2, 0, pulselen); // Move the left lower eyelid forward
    pwm.setPWM(servo3, 0, SERVOMAX - pulselen + SERVOMIN); // Move the right lower eyelid backward
    delay(1);
  }
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    pwm.setPWM(servo4, 0, SERVOMAX - pulselen + SERVOMIN);
    pwm.setPWM(servo5, 0, pulselen);
    pwm.setPWM(servo2, 0, pulselen);
    pwm.setPWM(servo3, 0, SERVOMAX - pulselen + SERVOMIN);
    delay(1);
  }
}

Thank you guys for helping :slight_smile: