Random led time on/off using millis()

hey all,
I am totally confused on millis() and random() functions, and maybe its because i can't do what I'm try to do with them. I have 2 separate plastic crows that were bought during halloween from a store several years back that have shorted boards. the heads turned (0-180), the eyes flashed (leds), and a "cawing" sound came through the 1w speaker. I figured i can just program a nano to control both crows; have the eyes blink at different intervals for different lengths of time, have the head turn to different angles at different speeds and stay for different lengths of time, and (at some point) add sound, a PIR, and a "irritated" mode that speeds up all actions.

I KNOW WAY BEYOND WHAT I SHOULD BE TRYING TO DO MYSELF

So I have the code for eyes done out and it does somewhat do what I am looking for, but it stays locked during each loop on the random numbers that were chosen during the first run through. I want it to change everytime. can anyone tell me where I am going wrong and how to fix it?

I've read A LOT of other posts, reseached online, and watch tons of YouTube videos but now am more confused than ever. I'll also probably runs into the issue when it comes time for adding the servos.

thanks in advance - I'm new so it wouldn't let me upload the sketch

unsigned long currentTime = millis();

const int crow1eyes = 2;   // pin for crow 1 eyes
unsigned long crow1on = random(1000, 5000);   // OFF time for crow 1 eyes
unsigned long crow1off = random(50, 400);   // ON time for crow 1 eyes
int crow1State = HIGH;   // starting state of crow 1 eyes
unsigned long PrevEyeTime1;   // time used by code to keep track of crow 1 time

const int crow2eyes = 3;   // pin for crow 2 eyes
unsigned long crow2on = random(1000, 5000);   // OFF time for crow 2 eyes
unsigned long crow2off = random(50, 400);   // ON time for crow 2 eyes
int crow2State = HIGH;   // starting state of crow 2 eyes
unsigned long PrevEyeTime2;   //time used by code to keep track of crow 1 time

void setup() {
  pinMode(crow1eyes,OUTPUT);  //sets eyes pin as output
  digitalWrite(crow1eyes,crow1State);   //sets initial state
  pinMode(crow2eyes,OUTPUT);   //define eyes pin as output
  digitalWrite(crow2eyes,crow2State);   //sets initial state
}

void loop() {
  currentTime = millis();
if (crow1State == HIGH){
  if(currentTime - PrevEyeTime1 >= crow1on){   
    crow1State = LOW;  //changes the state of eyes
    PrevEyeTime1 = currentTime;}  // remembers current time
 }
 else {   
    if(currentTime- PrevEyeTime1 >= crow1off){     
    crow1State = HIGH;   //changes the state of eyes
    PrevEyeTime1 = currentTime;}   //remembers current time
 }
 digitalWrite(crow1eyes, crow1State);// turns the eyes ON or OFF
 
 if (crow2State == HIGH ) {
    if(currentTime - PrevEyeTime2 >= crow2on) {   
    crow2State = LOW;   //changes the state of eyes
    PrevEyeTime2 = currentTime;}   //remembers current time
 }
 else {   
    if (currentTime - PrevEyeTime2 >= crow2off) {     
    crow2State = HIGH;  // changes the state of eyes
    PrevEyeTime2 = currentTime;}  // remembers current time
 }
 digitalWrite(crow2eyes, crow2State);   // turns the eyes ON or OFF

}

Welcome to the forum

Your topic was MOVED to its current forum category as it is more suitable than the original

Posting your code here in code tags was the right thing to do

You have to call random again in the loop.

You could call it after a value is used, so it would be fresh the next time.

A common mistake is thinking that this

unsigned long crow2on = random(1000, 5000); 

establishes a relationship, it does not. It sets the value once, and that value sticks until you

  crow2on = random(1000, 5000); 

somewhere in your code.

You will always get the same sequence of random numbers, but that's a subtle tea we can deal with after you notice it… if you do.

HTH

a7

You could

const int eye1OnMinimum = 100;
const int eye1OnMaximum = 500;

and so forth, at the top of you program, then use those constants everywhere you need them.

a7

Haha, that's what makes this so much fun.Welcome to the forum BTW!

I think you are on to a good start and it looks like a interesting project.

I am sure you will surprise yourself.

a7

Thanks for the help A7. Which way would be better in the long run? Keep in mind I will be adding code for both servos to move the head in pretty much the same fashion, so consistency would help a lot.

Would I also have to call out the random off time as well?

What would that code look like in the loop?

Those lines making the constants woukd go at the top of your program, outside any function definition where your other global variables are now.

Later when you execute the line of code to get a new random number in the range, you could write

  crow1on = random(eye1OnMminimum, eye1OnMaximum);

It's just for convenience and making numbers obvious so you don't need to go looking in the code to change "magic" numbers or accidentally change a number that looks like you want to but you really don't.

a7

yeah I'm not sure what I did wrong but it doesn't seem to be doing anything.

If you think you understood and followed my idea, post the code and say what it doesn't do that it should or is doing that it shouldn't. For anything that isn't what you want it to be at this stage.

a7

I didn't mean that your idea wasn't working I just meant that I probably didn't understand or implement it correctly.
This is what I did...

unsigned long currentTime = millis();

const int crow1eyes = 2;   // pin for crow 1 eyes
unsigned long crow1On;   // OFF time for crow 1 eyes
unsigned long crow1Off;   // ON time for crow 1 eyes
int crow1State = HIGH;   // starting state of crow 1 eyes
unsigned long PrevEyesTime1;   // time used by code to keep track of crow 1 time
const int eyesOnMinimum = 1000;
const int eyesOnMaximum = 5000;
const int eyesOffMinimum = 50;
const int eyesOffMaximum = 500;

const int crow2eyes = 3;   // pin for crow 2 eyes
unsigned long crow2On;   // OFF time for crow 2 eyes
unsigned long crow2Off;   // ON time for crow 2 eyes
int crow2State = HIGH;   // starting state of crow 2 eyes
unsigned long PrevEyesTime2;   //time used by code to keep track of crow 1 time


void setup() {
  pinMode(crow1eyes,OUTPUT);  //sets eyes pin as output
  digitalWrite(crow1eyes,crow1State);   //sets initial state
  pinMode(crow2eyes,OUTPUT);   //define eyes pin as output
  digitalWrite(crow2eyes,crow2State);   //sets initial state
}

void loop() {
currentTime = millis();
crow1On = random(eyesOnMinimum, eyesOnMaximum);
crow1Off = random(eyesOffMinimum, eyesOffMaximum);
crow2On = random(eyesOnMinimum, eyesOnMaximum);
crow2Off = random(eyesOffMinimum, eyesOffMaximum);

if (crow1State == HIGH){
  if(currentTime - PrevEyesTime1 >= crow1On){   
    crow1State = LOW;  //changes the state of eyes
    PrevEyesTime1 = currentTime;}  // remembers current time
    
}
 else {  
    if(currentTime- PrevEyesTime1 >= crow1Off){     
    crow1State = HIGH;   //changes the state of eyes
    PrevEyesTime1 = currentTime;}   //remembers current time
}
 digitalWrite(crow1eyes, crow1State);// turns the eyes ON or OFF

 if (crow2State == HIGH ) {
    if(currentTime - PrevEyesTime2 >= crow2On) {   
    crow2State = LOW;   //changes the state of eyes
    PrevEyesTime2 = currentTime;}   //remembers current time
}
 else {   
    if (currentTime - PrevEyesTime2 >= crow2Off) {     
    crow2State = HIGH;  // changes the state of eyes
    PrevEyesTime2 = currentTime;}  // remembers current time
}
 digitalWrite(crow2eyes, crow2State);   // turns the eyes ON or OFF
}

OK, close but not exactly.

I put your code with the slight change necessary (fixed onyl crow 1) into the wokwi simulator:

You will see all I did was to move the calls to get new random numbers to where I said to put them in post #4 above. Sorry, I could have made that fleshier. :expressionless:

I also "auto formatted" your code. It is a good habit if you haven't yet adopted a style from the several that are popular. In the IDE, the auto format tool does an OK job and makes reading the code (and spotting cetrain kinds of errors) way easier. Until you know more and can choose for yourself, the format it enforces is fine. Many ppl use it more or less as it is.

Oh, I also cranked down the time constants you manifested nicely. Life is too short even for 5 second crow eye blink testing.

Here's all your code with the tiny change. The effect of your misplaced random calls was interesting but with my beach ride coming by soon I'll leave it on the "never gonna" list. :wink:

// https://forum.arduino.cc/t/random-led-time-on-off-using-millis/979561

unsigned long currentTime = millis();

const int crow1eyes = 2;   // pin for crow 1 eyes
unsigned long crow1On;   // OFF time for crow 1 eyes
unsigned long crow1Off;   // ON time for crow 1 eyes
int crow1State = HIGH;   // starting state of crow 1 eyes
unsigned long PrevEyesTime1;   // time used by code to keep track of crow 1 time

// changed these for testing, life too short!
const int eyesOnMinimum = 100;
const int eyesOnMaximum = 500;
const int eyesOffMinimum = 100;
const int eyesOffMaximum = 200;

const int crow2eyes = 3;   // pin for crow 2 eyes
unsigned long crow2On;   // OFF time for crow 2 eyes
unsigned long crow2Off;   // ON time for crow 2 eyes
int crow2State = HIGH;   // starting state of crow 2 eyes
unsigned long PrevEyesTime2;   //time used by code to keep track of crow 1 time


void setup() {
  Serial.begin(112500);
  Serial.println("random eyes!\n");

  pinMode(crow1eyes, OUTPUT); //sets eyes pin as output
  digitalWrite(crow1eyes, crow1State);  //sets initial state
  pinMode(crow2eyes, OUTPUT);  //define eyes pin as output
  digitalWrite(crow2eyes, crow2State);  //sets initial state

// for testing
  crow1On = 1000; crow1Off = 200;
  crow2On = 1333; crow2Off = 333;
  
}

void loop() {
  currentTime = millis();

/* NOT every time you loop!
  crow1On = random(eyesOnMinimum, eyesOnMaximum);
  crow1Off = random(eyesOffMinimum, eyesOffMaximum);
  crow2On = random(eyesOnMinimum, eyesOnMaximum);
  crow2Off = random(eyesOffMinimum, eyesOffMaximum);
*/

  if (crow1State == HIGH) {
    if (currentTime - PrevEyesTime1 >= crow1On) {
      crow1State = LOW;

// just every time you actually use a random value, get a new one for next time
      crow1On = random(eyesOnMinimum, eyesOnMaximum);

      PrevEyesTime1 = currentTime;
    }

  }
  else {
    if (currentTime - PrevEyesTime1 >= crow1Off) {
      crow1State = HIGH;

      crow1Off = random(eyesOffMinimum, eyesOffMaximum); 

      PrevEyesTime1 = currentTime;
    }
  }
  digitalWrite(crow1eyes, crow1State);

  if (crow2State == HIGH ) {
    if (currentTime - PrevEyesTime2 >= crow2On) {
      crow2State = LOW;
      PrevEyesTime2 = currentTime;
    }
  }
  else {
    if (currentTime - PrevEyesTime2 >= crow2Off) {
      crow2State = HIGH;
      PrevEyesTime2 = currentTime;
    }
  }
  digitalWrite(crow2eyes, crow2State);
}

HTH and what's next?

a7

2 Likes

That's perfect! Awesome! Exactly what I was looking for!

How would I go about adding an "irritated mode" triggered by a pir that speeds up the process times 2, 3, or 4?

I will start a new thread for the servo issues I'm having. I need it to work in the same fashion. New angle at different intervals, speeds up when triggered by the pir

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