Repeating an action over a random amount of time.

Big fleas have little fleas upon their backs to bite them, and little fleas have lesser fleas, and so ad infinitum...

Yes. From Jonathon Swift from between the time when Democritus “invented” atoms and the rest of the world remembered that and got on with modern science.

caveat google.

a7

Erik_Baas:
I ran a little test to see what values to expect from analogRead():

  for (int i = 1; i <= 10; i++) {

for (int pin = A0; pin <= A5; pin++) {
      Serial.print(analogRead(pin));
      Serial.print("\t");
    }
    Serial.print("\n");
  }



and I did not like the results very much:


380 337 307 283 290 359
368 352 333 311 283 325
344 343 337 323 295 308
319 324 325 323 318 310
306 309 311 312 303 301
298 299 301 302 299 304
297 294 293 291 307 298
296 292 290 287 283 291
295 291 288 285 281 295
289 287 285 284 274 289




Same loop, 100 times instead of 10, restarted Uno 3 times, shown on the serial plotter:


![analogRead(A0..A5) 4 times.jpg|1046x346](upload://fIVEHtWepxrNSjQjVminCfhyXer.jpeg)

Any amount of randomness in the seed is better than the none you are currently feeding it.

If you want to fully use t he randomness, you can just run analogRead multiple times, take the lowest couple bits, and stick them all together to make as random a seed as you can get.

const uint8_t SEED_GEN_BITS = 2;
const unsigned long SEED_GEN_MASK = ~((1<<SEED_GEN_BITS)-1);

unsigned long seed = 0;
for( int i = 0; i<sizeof(unsigned long)/SEED_GEN_BITS; ++i )
{
  seed |= (uint32_t)(analogRead(pin) & SEED_GEN_MASK) << (i*SEED_GEN_BITS);
}

Your task now, should you choose to accept it, it to understand what I am doing in this code and why I have chosen to write it this way. :slight_smile:

Erik_Baas:
One needs a prng to seed the prng, so to speak... :wink:

Jokes aside, randomness is hard for computers since they are strictly mathematical and deterministic. PRNGs are a computationally easy way to get the appearance of randomness, but true randomness can only come from the environment outside of the computer. It is expensive to do this, so seeding a PRNG allows you to get away with only having to pull true randomness once for the seed, then the PRNG formula can calculate all the following numbers easily.

roguemike:
So how would I go about linking the "randomness" to the analog pin...

You can't. Floating is not equivalent to random. It's fool's errand to believe otherwise.

This is a good choice.

Measuring the watchdog timer using the processor's clock is a good choice.

I think most of us understand the difference between pseudo randomness and actual randomness here, Coding Bradly. Were talking about relative random here, and a lot of the guys are even using the tern PRNG at that. I'm not looking to have a discussion about true random. I would go make myself a Geiger Counter if I wanted to take it to that level.

Anywawys, I hope you guys aren't tired of answering my dumb questions yet lol, but if you are bored enough to help bridge one last gap I have one last question here...
Again the desired intention is to jitter the servo 5 degrees, quickly back and forth between the random 2 to 5 second intervals.
Using Millis, how would I go about saying the equivalent of:
Move (VariableDegrees + 5)
delay(50)
Move (VariableDegrees - 5)
delay(50)
Update (VariableDegrees) every (2 to 5 seconds)

I'll include my flawed code attempt so you can see where I was going, for context. But this isn't working as intended because I wanted a back and fourth jitter of about every, lets say 5 milliseconds. With this code I get a +5 jitter and a -5 jitter, so I feel like I'm on the right path but it updates the main servo angle every cycle of the jitter instead of the randomTime which I would have thought I coded correctly in the first IF statement. Hope I'm explaining this well enough, but I might be so far into the trees here that I cant see the forest here.
I moved this to Serial.print so this can be done on the monitor of anyone who cares, without building the device.

unsigned long jitterTime = 0;

long randX; //ServoX
long randY; //ServoY
long randTime; //Time

void setup() {

  Serial.begin(9600);

}

void loop() {

  unsigned long currentTime = millis();
  randTime = random(2000, 5000); //Min time 2 seconds, max time 5 seconds

  if (currentTime >= randTime){
    Serial.print(randX);
    Serial.println(" X axis"); //Moves ServoX
    Serial.print(randY);
    Serial.println(" Y axis"); //Moves ServoY
    randX = random(20, 160); //Moved into loop so it only updates Var when being used
    randY = random(120, 180); //Moved into loop so it only updateds Var when its been used
    currentTime = 0;
  }

    if (currentTime - 10 >= jitterTime){         //if (currentTime - 5 >= jitterTime & currentTime +5)
      Serial.print(randX + 5); //Moves ServoX
      Serial.println("X+");
      Serial.print(randY + 5); //Moves ServoY
      Serial.println("Y+");
  }

      if (currentTime - 5 >= jitterTime){
      Serial.print(randX - 5); //Moves ServoX
      Serial.println("X-");
      Serial.print(randY - 5); //Moves ServoY
      Serial.println("Y-");
  }
}

With this code I'm getting one "jitter" (+5, then -5) and then a new Servo coordinate (randX and randY) where I "should" be getting several jitters before a new Servo coodinate to base my -/+ 5 jitter coordinates off of.
Also this may be flat out, a bad way to go about coding this so correct me if you think there is a much better way to do this. I just try my best to come up with a solution before asking the forums. Don't want to waste your time and have you do all my work for me. I would hardly learn anything if I did anyways.

To recap, I want to jitter the servo by +5 then -5 degrees (based off the randX and randY coordinate) every 10 milliseconds (might need to be adjust for actuation time but yeah). And I'm not using "delay()" for the obvious reasons here.

I did think of an easy way to use "delay()" by just spacing out the random main servo adjustments by a random amount of "jitters" instead of "time". But as this is mostly a learning project, once I was told about millis I wanted to solve this with the more flexible yet challenging way of using them rather than a simple delay().
So if you are enjoying explaining some of these mechanisms to a noob please continue! But don't sweat it since I have it working "good enough" : )
You guys have been great.

Using Millis, how would I go about saying the equivalent of:
Move (VariableDegrees + 5)
delay(50)
Move (VariableDegrees - 5)
delay(50)
Update (VariableDegrees) every (2 to 5 seconds)

See reply #1

You need to wrap your short timing periods (jitters) in a longer timing period

For sure, as you have been the biggest help here. I just don't know how, or have the programing knowledge / vocabulary to translate your pseudo code into real code. I love putting the blocks together myself but I don't even know what blocks to use here.
I don't know how to make your pseudo code, into read code, but it does make general sense to me.
Millis is a great new tool you gave me and I'm happy for that, but I am likely missing some other functions here that would connect the rest of the dots.

void loop() {

  unsigned long currentTime = millis();
  randTime = random(2000, 5000); //Min time 2 seconds, max time 5 seconds

  if (currentTime >= randTime){
     // do stuff
    currentTime = 0;
  }

    if (currentTime - 10 >= jitterTime){ 
    // do stuff
  }

      if (currentTime - 5 >= jitterTime){
    // do stuff
  }
}

Just have a quick look at your loop(), stripped back to the bare bones, and consider the value of currentTime and how it changes.

When loop() runs for the first time, millis() will return something close to zero, so the first if condition won't be true.

What about the second if condition? Let's imagine currentTime is 7, say. What do you expect to occur?

As time goes on, millis() will return ever-greater values, yea, 10,000 or more up until somewhere in day 49 when it will reset to zero. What should your if conditions do once currentTime is always >= randTime?

Hint: you don't actually want to take an action based on absolute value of time. You want to take action based on the time elapsed since you last did something.

I mostly agree. I want to make code based on elapsed time, not total time, but once the time overflows after 50 days, which would never happen in this specific case (as it would be unplugged before that amount of time), but still good practice to pretend it could none the less. But I don't know how to go about writing code based on elapsed time. I think that would be a big help. Just hearing that I might be able to scratch up some forums on the subject on Google. So I appreciate the nudge in the right direction there. Is there a simple line to reset Time to 0?

The first few loops and the day 49 roll over wasn't a big concern here, at least not yet. I was more so confused why I get one stutter per "randX" and "randY" servo ordinates. I assumed with this code it would only update the random X and Y when it was used. But instead it seems to be running the IF statement of each IF statement every time.

I feel like I'm rambling about pointless solutions here when I already have a working concept with "delay()" so I'm not trying to beat a dead horse here by any means. I just wondered about doing this a better way. Hope this isn't making a few of you face palm for me.

It is easier to write it than describe it so try this

unsigned long currentTime;
unsigned long longStartTime;
unsigned long shortStartTime;
unsigned long longPeriod;
unsigned long shortPeriod;
byte servoPosition;
int servoJitter = 5;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  longStartTime = millis();
  shortStartTime = millis();
  shortPeriod = random(500, 200);
  longPeriod = random(2000, 5000);
  servoPosition = random(20, 160);
  Serial.print("initial servo position : ");
  Serial.println(servoPosition);
}

void loop()
{
  currentTime = millis();
  if (currentTime - longStartTime >= longPeriod)
  {
    longStartTime = currentTime;
    longPeriod = random(2000, 10000);
    servoPosition = random(20, 160);
    Serial.print("new servo position : ");
    Serial.println(servoPosition);
  }
  if (currentTime - shortStartTime >= shortPeriod)
  {
    shortStartTime = currentTime;
    shortPeriod = random(200, 1000);
    servoPosition += servoJitter;
    servoJitter = -servoJitter;
    Serial.print("\tservo jittered to : ");
    Serial.println(servoPosition);
  }
}

I will leave it to you to add the actual servo commands and adjust the timing but try it as it is first

Awesome. I usually just need a few terms or pointed in the right direction. I'll give this a go tomorrow. I'll post the fully working code by the end of this for curiosity sake, and for any possible future Google searches that might land someone here.

I usually just need a few terms or pointed in the right direction

From experience it is very difficult to know how to provide advice at the right level, which can lead to frustration on the part of both parties. What might be obvious to me may not be obvious to you so sometimes a working but incomplete example is better. There is also the problem of when several people provide help and the thread becomes disjointed but, of course, the fact that you may get help from several people is also an advantage

Obviously come back if you have any queries and I look forward to seeing your code.

100% Bob. Sometimes I feel like I explain my issue pretty well, but the I re explain it, and it get messy, and I try to clean that up and it gets more messy! And... and... and. Well you know.
This forum has been a lot more help than the first time I used it. To be fair, the fist time I also wasn't using it as well as I should have been, but if I'm being honest, while the make community is usually super cool the coding community is rather arrogant.
I personally have to try extra hard to stay positive in coding forums and read responses as well intentioned.
Lets all just try to remember that were here to learn and help and maybe make the world a better place.
And you can be sure to see the right (int, long, ect) when I finalize my simple little project : )
I want to reflect all the help I was given, and you helped a lot here. And I enjoyed the discussion about the best way to go about pseudo random numbers. Where math kinda, almost, maybe meets philosophy haha.
I try to navigate these posts towards my own questions, but also questions people may have in the future since I also come across plenty of old forum posts in my searches.
But this post took me from a "working kinda, program" to a better structured code with plenty of terms learned along the way project <3

@roguemike, I also had a go at your sketch but got distracted before I posted it. I'm putting it up to compare/contrast with Bob's in case there is anything interesting to be drawn from it.

I tweaked the serial baud rate to 115200 for my terminal program, and made the jitter interval large since that's easier to follow manually, so please be aware of that!

unsigned long jitterTime = 0;
unsigned long lastUpdate = 0;
unsigned long nextUpdate = 0;

long randX; //ServoX
long randY; //ServoY
int jitter = 5;

#define JITTER_INTERVAL 1000

void setup() {
  Serial.begin(115200);
}

static void setServoAxis(const char *axis, unsigned long deg)
{
  Serial.print(deg);
  Serial.print(" ");
  Serial.print(axis);
  Serial.println(" axis");
}

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

  if (!nextUpdate || currentTime > nextUpdate) {
    Serial.print("#### UPDATE ");
    Serial.println(currentTime);
    randX = random(20, 160);
    randY = random(120, 180);
    setServoAxis("X", randX);
    setServoAxis("Y", randY);
    nextUpdate = currentTime + random(2000, 5000);
    lastUpdate = currentTime;
  } else  if (!((currentTime - lastUpdate) % JITTER_INTERVAL)) {
    Serial.print("#### JITTER ");
    Serial.println(currentTime);
    setServoAxis("X", randX + jitter);
    setServoAxis("Y", randY + jitter);
    jitter *= -1;
  }
}

This forum has been a lot more help than the first time I used it. To be fair, the fist time I also wasn't using it as well as I should have been, but if I'm being honest, while the make community is usually super cool the coding community is rather arrogant.
I personally have to try extra hard to stay positive in coding forums and read responses as well intentioned.
Lets all just try to remember that were here to learn and help and maybe make the world a better place.

I think there's a definite mindset which goes with the territory a bit, unfortunately. I think it's a combination of the personality type you need to want to work with computers in the first place, amplified by years of working with the most pedantic, unimaginative, and literal-minded creatures you could wish to meet (i.e. computers). An old boss of mine once told me: "half of being a good software engineer is being a right tedious b******", and experience hasn't proven him wrong thus far.

Anyway, I think your approach is the right one, and it's the approach I try to take too :slight_smile:

roguemike:
...Were talking about relative random here...

You've performed one experiment under uncontrolled conditions. It's impossible for you to know what you're talking about. If you had read the article referenced in the first link you'd know what you're actually discussing is no better than randomSeed(4).

roguemike:
I'm not looking to have a discussion about true random.

Didn't even bother to click the first link. Got it.

I won't waste any more of your time or mine.

UKHeliBob
Not sure why Bob, but while your code looked beautiful in the serial monitor window, my servos would lock up. Pinned trying to push past angle 180. It still looked good on the serial monitor window, so I'm not sure as to why the servo wasn't following the angle displayed on the monitor. I will keep playing with the code later, for the sake of curiosity.

tomparkin
Yeah, it's just a bit unexpected and disappointing when so much of the Maker community is so great. But a few bad apples don't spoil the bunch to me. Learned a lot more than I expected here and had a good time doing it. You are all so wonderful taking your own stabs at this code!

While my original goal wasn't to stop using "delay()" I did/do want to practice avoiding it more in the future now that I've been made aware of its short comings here. For sake of learning I might still try to make this toy work with milis() but for now I want to put a pin in this project and call it done enough. (I got an automated music box to make and a cat targeting squirt bottle parts finally came in for). So I moved to the idea of setting the X/Y servos main position, and jittering through a For Loop with the aforementioned delay(). I guess there is a time and a place for delay() still and this dose the desired job and saves on the complexity and length of the code.

I tried using different data types here, so I think I used the correct ones, but if I'm wrong you can definitely point it out to me. Don't want to assume I've learned while continuing to use needlessly large data types.
I also moved the "digitalWrite(laser, HIGH)" into the setup because it seemed pointless to loop it. I don't know if this is frowned upon but it seems better to me. I'll post the actual code, and a serial monitor friendly version separately below.

Thanks guys, this was fun! I would post a video of it but I don't know if that breaks some rules. Some forums I've been a part of consider it self promotion and I don't know all the rules here : )
As far as the jittering goes, this grabs the cats attention SO much better. It was definitely worth the extra effort. Went from 1 cat being kind of interested to all 3 being fixated haha. Even the shy cat is having a blast.

Actual code:

#include <Servo.h>

Servo servoX;
Servo servoY;
byte laser = 2;

byte randX;     //ServoX position 20-160
byte randY;     //ServoY position 120-180
word randJit;   //Jitter iterations between new X and Y angles given 200-500, 

void setup() {
  pinMode(laser, OUTPUT);       //Laser on pin 2
  digitalWrite(laser, HIGH);    //Turns the laser on
  servoX.attach(7);             //ServoX on pin 7
  servoY.attach(8);             //ServoY on pin 8
  randomSeed(analogRead(0));    //Mostly to start more arguments in the forum
}

void loop() { 
  randX = random(20, 160);       //Keeps the laser from hitting the mount at position 0 to 19 and 161 to 180
  randY = random(120, 180);      //Keeps the laser from pointing to far up walls or the at the ceiling
  randJit = random(200, 500);    //How many full jitter itterations before the servos main angle is adjusted.
  
  servoX.write(randX);            //Moves ServoX to main X position
  servoY.write(randY);            //Moves ServoY to main Y position

  //Loop that jitters the servo +1 then -1 degress (randJit) 200 to 500 times
  for (int i = 0; i <= randJit; i++) {
    servoX.write(randX + 1);
    servoY.write(randY + 1);
    delay(2);
    servoX.write(randX - 1);
    servoY.write(randY - 1);
    delay(2);
  }
}

Serial Monitor friendly code:

#include <Servo.h>

Servo servoX;
Servo servoY;
byte laser = 2;

byte randX;     //ServoX position 20-160
byte randY;     //ServoY position 120-180
word randJit;   //Time between new X and Y angles given 2 to 5 times in this Serial version
                //Doesn't need to be a "word" in the Serial Monitor code but in the actual code it does
void setup() {
  Serial.begin(9600);
  pinMode(laser, OUTPUT);     //Laser on pin 2
  digitalWrite(laser, HIGH);  //Turns the laser on
  servoX.attach(7);           //ServoX on pin 7
  servoY.attach(8);           //ServoY on pin 8
  randomSeed(analogRead(0));  //Mostly to start more arguments in the forum
}

void loop() { 
  randX = random(20, 160);    //Keeps the laser from hitting the mount at position 0 to 19 and 161 to 180
  randY = random(120, 180);   //Keeps the laser from pointing to far up walls or the at the ceiling
  randJit = random(2, 5);     //How many full jitter iterations before the servos main angle is adjusted.
  
  Serial.print(randX);                   //Moves ServoX to main X position
  Serial.println(" Main Servo X angle");
  Serial.print(randY);                   //Moves ServoY to main Y position
  Serial.println(" Main Servo Y angle");

  //Loop that jitters the servo +1 then -1 degrees (randJit) 2 to 5 times
  for (int i = 0; i <= randJit; i++) {
    Serial.print(randX + 1);
    Serial.println(" +JitterX+");
    Serial.print(randY + 1);
    Serial.println(" +JitterY+");
    delay(20);
    Serial.print(randX - 1);
    Serial.println(" -JitterX-");
    Serial.print(randY - 1);
    Serial.println(" -JitterY-");
    delay(20);
  }
}

Plenty of people post links to video so post one if you have it. Besides I thought cat pictures was why the internet was created :wink:

Well if its not frowned upon, then I'll post one right as I make it here in a day or two! <3

I will keep playing with the code later, for the sake of curiosity.

Post it here and I will take a look