Progress on BB-9E code and questions about "Millis"

Hello!
This is my second Arduino code. I have created a BB-9E robot from Star Wars: The Last Jedi. I have been working on the lighting and movement of the head, and have worked a bit on the code. Essentially, I want the Arduino to complete the following:

Head servo: Every 50 to 5000 milliseconds, turn a random amount of degrees. Every 2 to 5 turns, enable an “Alarm” function for 800 to 2000 milliseconds. When the Alarm is going off, have the servo stay put.
Red “Eye” LED: When the “Alarm” is not on, have the PWM be at 20. When the “Alarm” function is enabled, fade to 255 PWM. When the “Alarm” function is finished, fade back to 20 PWM.
Side RGB LEDs When the “Alarm” function is not on, the RGB is at 0, 0, 255, (so plain blue.) When the “Alarm” function is activated, flash between red (255, 0, 0,) for 100 milliseconds and blue (0, 0, 255,) for 300 milliseconds until the Alarm turns off. When the “Alarm” is not on, it flashes back to and stays at blue.

An example of BB-9E can be found in this Verizon commercial: Verizon Commercial STAR WARS 8 BB 8 vs BB 9E for The Last Jedi - YouTube. When BB-9E notices an enemy, his alarm goes off. His red “eye” grows bright and his side LEDs flash like the top of police car. Since I won’t have control over when BB-9E’s alarm goes off, I’d like him to have it go off randomly to make him look more animated.

Right now my code is a big mess but I tried to organize it. I’ve attempted to write up my own code for a servo using the Millis function but I have yet to test it as I am still figuring out how to format it. Would someone be able to look over the Servo sections of the code and please give me feedback?
The second part I have worked on is the Red “Eye” LED. I copied some code and kinda understand it but I still need to work out an “Alarm” function for it to follow-tests just make it fade from being dim to bright.
Is there a way for me to add a function to be enabled/disabled when I need it for the “Alarm” function? How would I go about writing the code?
Once I get a better understanding of how Millis work and get the Alarm function working, I will start working on the side LEDs. Hopefully it will be fairly simple by then.

Thanks for the help and please ask questions as I am probably a bit confusing. I have my code attached to this post.

Edit: here is the code

//-=-=-=-=LIBRARY-=-=-=-=

//:::::Servo Settings:::::

#include <Servo.h> 
 
Servo myservo;  // create servo object to control a servo 
                // twelve servo objects can be created on most boards

//:::::Eye LED Settings:::::
const byte pwmLED = 3;

//:::::Side LED Settings:::::

int redPin = 11;
int greenPin = 10;
int bluePin = 9;
 
//-=-=-=-=DEFINITIONS-=-=-=-=

//:::::Servo Settings:::::

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

 // servo gets their own tracking variable
unsigned long previousMillisSERVO5=0;

// starting interval for servo
int intervalSERVO5 = (random(50,5000));

// servo gets a state variable
boolean SERVO5state = false;     // the servo will turn ON in the first iteration of loop()

//:::::Eye LED Settings:::::

// define directions for LED fade
#define UP 0
#define DOWN 1
 
// constants for min and max PWM
const int minPWM = 20;
const int maxPWM = 255;
 
// State Variable for Fade Direction
byte fadeDirection = UP;
 
// Global Fade Value
// but be bigger than byte and signed, for rollover
int fadeValue = 0;
 
// How smooth to fade?
byte fadeIncrement = 5;
 
// millis() timing Variable, just for fading
unsigned long previousFadeMillis;
 
// How fast to increment?
int fadeInterval = 50;

//:::::Side LED Settings:::::

//If not using common anode, put "//" in front of text below.
#define COMMON_ANODE
 
//-=-=-=-=SET-UP-=-=-=-=

void setup() 
{ 
//:::::Servo Settings:::::

  myservo.attach(5);  // attaches the servo on pin 5 to the servo object 
// put pwmLED into known state (off)
  analogWrite(pwmLED, fadeValue); 
} 

//:::::Eye LED Settings:::::


//:::::Side LED Settings:::::

//-=-=-=-=PROGRAM-=-=-=-=

//:::::Servo Settings:::::

             
//:::::Eye LED Settings:::::

void doTheFade(unsigned long thisMillis) {
  // is it time to update yet?
  // if not, nothing happens
  if (thisMillis - previousFadeMillis >= fadeInterval) {
    // yup, it's time!
    if (fadeDirection == UP) {
      fadeValue = fadeValue + fadeIncrement;  
      if (fadeValue >= maxPWM) {
        // At max, limit and change direction
        fadeValue = maxPWM;
        fadeDirection = DOWN;
      }
    } else {
      //if we aren't going up, we're going down
      fadeValue = fadeValue - fadeIncrement;
      if (fadeValue <= minPWM) {
        // At min, limit and change direction
        fadeValue = minPWM;
        fadeDirection = UP;
      }
    }
    // Only need to update when it changes
    analogWrite(pwmLED, fadeValue);  
 
    // reset millis for the next iteration (fade timer only)
    previousFadeMillis = thisMillis;
  }
}

//-=-=-=-=LOOP-=-=-=-=

void loop() 
{ 
// get the current time, for this time around loop
  // all millis() timer checks will use this time stamp
  unsigned long currentMillis = millis();
    
  doTheFade(currentMillis);
  for(pos = 0; pos <= 180; pos += 1) // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos'

    (current time - time motor was started > intervalSERVO5) // NOTE: Still figuring out variables for this :P
          for(pos = 180; pos>=0; pos-=1)     // goes from 180 degrees to 0 degrees 
    (current time - time motor was started > intervalSERVO5)//As noted before, not sure what "current time" and "time motor was started" should be called and adjusted to
        myservo.write(pos);              // tell servo to go to position in variable 'pos' 
  {                   

//-=-=-=-=PROGRAM-=-=-=-=

//:::::Servo Settings:::::

             
//:::::Eye LED Settings:::::


//:::::Side LED Settings:::::
void setColor(int red, int green, int blue)
{
  #ifdef COMMON_ANODE
    red = 255 - red;
    green = 255 - green;
    blue = 255 - blue;
  #endif
  analogWrite(redPin, red);
  analogWrite(greenPin, green);
  analogWrite(bluePin, blue);  
}

BB-9E_sketch.ino (3.81 KB)

Please post your code accroding to forum guidelines in the sticky post.

I've attempted to write up my own code for a servo using the Millis function

Why? A servo requires constant, precisely timed, pulses of much higher resolution than you can get with the millis timer. Why not use the servo library?

PaulRB:
Please post your code accroding to forum guidelines in the sticky post.

Ok, I edited the first post.

Grumpy_Mike:
Why? A servo requires constant, precisely timed, pulses of much higher resolution than you can get with the millis timer. Why not use the servo library?

Sorry, by “Millis function” is was hoping to find a way to avoid the delay function as I am running many things at once. The code a friend wrote for me was originally:

#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 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  delay(random(50,5000));
  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 
  } 
  delay(random(50,5000));
}

However, I was trying to rewrite the servo code going with the Millis delay. So the servo sections of my large chunk of code are:

#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 

 // servo gets their own tracking variable
unsigned long previousMillisSERVO5=0;

// starting interval for servo
int intervalSERVO5 = (random(50,5000));

// servo gets a state variable
boolean SERVO5state = false;     // the servo will turn ON in the first iteration of loop()

 myservo.attach(5);  // attaches the servo on pin 5 to the servo object 
// put pwmLED into known state (off)
  analogWrite(pwmLED, fadeValue); 
} 

void loop() 
{ 
// get the current time, for this time around loop
  // all millis() timer checks will use this time stamp
  unsigned long currentMillis = millis();
    
  doTheFade(currentMillis);
  for(pos = 0; pos <= 180; pos += 1) // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos'

    (current time - time motor was started > intervalSERVO5) // NOTE: Still figuring out variables for this :P
          for(pos = 180; pos>=0; pos-=1)     // goes from 180 degrees to 0 degrees 
    (current time - time motor was started > intervalSERVO5)//As noted before, not sure what "current time" and "time motor was started" should be called and adjusted to
        myservo.write(pos);              // tell servo to go to position in variable 'pos' 
  {

Since I was basing the servo code off of an LED code, I probably messed up, but I was reading various articles about Millis. I couldn’t seem to find something directly on servos that also have a program that sets a random amount of the millis you are counting up to. This topic seems to come close but I don’t want to redo my code if it is unrelated to what I want: https://forum.arduino.cc/index.php?topic=68305.0

The two lines your friend wrote like this:-

delay(15);                       // waits 15ms for the servo to reach the position

are totally unnecessary and will not work with all or any servo. Expecting a servo to move in 15mS is a bit silly.

I probably messed up,

Yes you have totally got the wrong idea about servos and controlling them. What you wrote will not help you one bit. Can you explain what the idea was you were thinking you were implementing? You either have the wrong idea or are not implementing to correctly.
Having two nested loops with the same loop index variable in each is wrong. I would suspect that the servo just judders all over the place.

Try this untested code as a basis for the head movement

/*
 * Head servo: Every 50 to 5000 milliseconds, turn a random amount of degrees
 */

#include <Servo.h>

Servo headServo;

unsigned long currentTime;
unsigned long headMovedTime;
unsigned long headMovePeriod = random(50, 5000);
byte headMoveCount = 0;

void setup()
{
  Serial.begin(115200);
  headServo.attach(9);
}

void loop()
{
  currentTime = millis();
  if (currentTime - headMovedTime >= headMovePeriod)  //time to move ?
  {
    headServo.write(random(0, 180));  //move servo
    headMovedTime = currentTime;  //save time of move
    headMovePeriod = random(50, 5000);  //period before next move
    headMoveCount++;  //increment head move counter
  }
}

Grumpy_Mike:
The two lines your friend wrote like this:-

delay(15);                       // waits 15ms for the servo to reach the position

are totally unnecessary and will not work with all or any servo. Expecting a servo to move in 15mS is a bit silly.
Yes you have totally got the wrong idea about servos and controlling them. What you wrote will not help you one bit. Can you explain what the idea was you were thinking you were implementing? You either have the wrong idea or are not implementing to correctly.
Having two nested loops with the same loop index variable in each is wrong. I would suspect that the servo just judders all over the place.

The idea I have is that I’ve got three programs that all work fine separately and I want them to work at the same time. One made by my friend for the servo to move randomly, another is to make side LEDs blink, and the other is to make the eye LED fade. I’ve researched that making these work at the same time would require me using Millis instead of delay. I also want to add the “Alarm” function, which is a value that contains code for the eye and lights that is started by the servo at random times. With the servo, I was trying to make the code start the servo to move, have the servo stop, record time, and then see how long it is until the recorded time matches the randomized time. When recorded=randomized, the servo starts moving again, stops, and begins the process over again.

UKHeliBob:
Try this untested code as a basis for the head movement

/*
  • Head servo: Every 50 to 5000 milliseconds, turn a random amount of degrees
    */

#include <Servo.h>

Servo headServo;

unsigned long currentTime;
unsigned long headMovedTime;
unsigned long headMovePeriod = random(50, 5000);
byte headMoveCount = 0;

void setup()
{
  Serial.begin(115200);
  headServo.attach(9);
}

void loop()
{
  currentTime = millis();
  if (currentTime - headMovedTime >= headMovePeriod)  //time to move ?
  {
    headServo.write(random(0, 180));  //move servo
    headMovedTime = currentTime;  //save time of move
    headMovePeriod = random(50, 5000);  //period before next move
    headMoveCount++;  //increment head move counter
  }
}

Thanks, I’ll have to test it, but this seems to be what I need for my code. I’ll test it in a few minutes and let you know how well it works.

The problem here is that you do not know how long it takes for a servo to reach a specific position. In fact you have no way of knowing when it has reached a specific position.

So I can't see how you can "record time" when the servo stops moving.