I’ll try ot make this quick.
I made a little cat toy out of 2 servos and a laser. An X and Y axis servo with a laser at their end.
I have a random number given for degrees to point the X and Y servos and a random number for the time between movements.
To better keep the cats attention, I want to add a little stutter to the servo jittering the laser back and forth a degree or two on each axis.
I know I could say "Jitter the servo a random amount of times before moving to the next general movement, but that feels kinda clunky and I like to attempt to learn a thing or two from these small projects. So how would you guys implement this idea of jittering the servos the entire time between the larger movements?
A second question I had was, are you able to read each random input from separate analog pins? Right now it seems that roughly 50% of the time only one servo moves instead of both, but this also could be a power issue since I’m running both micro servos and the laser off the Unos 5V output.
So what are your ideas on how to code this? And if there are any flaws in mine, feel free to point them out. It’s been a while since I’ve played with random numbers in Arduino.
#include <Servo.h>
Servo servoX;
Servo servoY;
int laser = 2;
long rand1; //ServoX
long rand2; //ServoY
long rand3; //Time between new X and Y angles given
void setup() {
pinMode(laser, OUTPUT); //Laser on pin 2
servoX.attach(7); //ServoX on pin 7
servoY.attach(8); //ServoY on pin 8
}
void loop() {
digitalWrite(2, HIGH); //Turns the laser on
rand1 = random(20, 160); //Keeps the laser from hitting the mount
rand2 = random(120, 180); //Keeps the laser from hitting walls or the ceiling
rand3 = random(2000, 5000); //Min time 2 seconds, max time 5 seconds
servoX.write(rand1); //Moves ServoX
servoY.write(rand2); //Moves ServoY
delay(rand3); //Delays for the random time generated
}
Before you enhance your sketch can I suggest that you tidy up the current one ?
For instance, why are rand1 and rand2 declared as longs when their value will never exceed 255 ? Why is rand3 an unsigned long when its value will never exceed 5000 ?
As to your possible enhancements, you will need to move to miilis() for timing as using delay() blocks the code from doing anything else. You will need 3 timing periods, an overall period during which the servos jitter and a timing period for each servo to implement the jitter periods
In psuedo code something like this :
start of loop()
use random to define the wait time
use random to define the x jitter period
use random to define the y jitter period
use random to define the base x position
use random to define the base y position
save the millis() value as the start time
save start time as x jitter start time
save start time as y jitter start time
move servos to base positions
while current time - start time >= wait time
if current time - x jitter start time > x jitter period
move x a small random amount
save current time as x jitter start time
end if
if current time - y jitter start time > y jitter period
move y a small random amount
save current time as y jitter start time
end if
end while
end of loop()
Take a look at Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE
I just used longs because that was in the Arduino tutorial page for Random Numbers and worked well enough. I do appreciate the pointer on just using an Int in this case for future knowledge. But as to why I did so, well it worked and I didn't want to fix what wasn't broken. Not trying to micro manage my code into the tiniest memory possible typically. I would rather spend time making things, than make things perfect.
As far as the While Loops go, I think that is what I was looking for. Just feels more tidy.
I'll play around with some While Loops tomorrow. Thank you 
Whilst there is nothing wrong with using a variable type capable of holding a value an order of magnitude (or two) larger than needed, it is not a habit that you should get into. For your current sketch you have plenty of memory available but that may not always be the case and there are other reasons for using as small a variable type as possible
As to the while loop in my pseudo code, like your variable types, it is not actually a good idea in general because it can cause problems by blocking code execution. As an example, what if you wanted two independent pairs of servos, each with their own timing ? The timing for one of the pairs would interfere with the timing of the other pair so a different technique would be preferable
But it is a case of horses for courses and for your needs a while loop should work OK
In this instance, the X and Y coordinates are intended to be spaced out by the same time, and update at the same time. I never thought about each axis running on their own random times. The cats are having a hard enough time tracking the laser with how fast the servos are, but as a learning exorcise that would be a fun obstacle to overcome.
Once you have got the overall timing loop working using millis() you can do whatever you like during the period as long as your code does not block the free running of the code so moving one or two servos independently makes little difference
@UKHeliBob "Whilst there is nothing wrong with using a variable type capable of holding a value an order of magnitude (or two) larger than needed, it is not a habit that you should get into."
LOL. TBC this is binary order of magnitude, and if I have to go out later I'll need to put on a clean shirt.
a7
I don't see you seeding the random number generator. The random() function is a pseudo-random number generator. it uses a mathematical function to create a sequence of numbers that appears random to our human evaluations. Without seeding, the function will always have the same starting point, meaning that its output sequence will be identical every time it is run. If you want it to run differently every time, you must seed it with something different every time the program is run, meaning you need to take information from an external source of noise (random() - Arduino Reference).
Ever watch a video game speedrun and hear them talk about "RNG manipulation"? That means the PRNG's formula is known and they've discovered ways of taking advantage of its predictability.
Without seeding, the function will always have the same starting point, meaning that its output sequence will be identical every time it is run
Just how clever do you think these cats are !
UKHeliBob:
Just how clever do you think these cats are !
Very.
The other members of my family own a lot of pets. They aren't stupid.
Well the main intention from this project is to learn. So just as maybe not "needing" to change to Int from Long, I am glad to learn that, and I do want to better understand the Random function. I was semi aware of this, as I never called to read an analog pin but as the project went on I kinda ran into the, it's not broken so lets not fix it mentality.
So how would I go about linking the "randomness" to the analog pin, or better yet. How, if possible do I tie each of the 3 random numbers I wish to generate to their own analog read pin? I know it might likely not be needed but doing so would make my tiny brain happy! 
I've seen the:
randomSeed(analogRead(0));
But I don't understand how it works. How my code knows to connect my Random function to this line of code.
I don't understand how it works.
The random() function will always output the same sequence of numbers depending on the starting point in the sequence, ie it is not truly random at all
An analogue pin not connected to anything will be floating at an unknown voltage somewhere between 0 and 5V. The parameter passed to randomSeed() determines where exactly the random() function starts to produce its "random" number, so effectively you are using a semi random number as the input to randomSeed() thus making it less likely that the random() function will produce the same sequence of numbers each time that the program is run
Note, however, that in practice the range of values returned by a floating analogue pin may not be as wide as 0 to 5V so the range of "random" values it returns may not be as wide as expected. For most applications, however, it is good enough
As there is only one random() function, seeded by randomSeed() if you use it, then it is not easily possible to create more than one random sequence of numbers
Ah I see. So I was using a pseudo random number but not from a pseudo random starting point. So it was the same random sequence over and over?
I think the last question I have here is. I have 3 inputs, each wanting a random number(X, Y and Time). If there is only one randomSeed() then are my 3 variables(X, Y and Time) going to be pretty similar to each other if they are checked for one after another? Or does the static on the pin change so fast that even being checked by the time the next line of code is ran that I will have a random enough number still?
So I was using a pseudo random number but not from a pseudo random starting point. So it was the same random sequence over and over?
Yes
If there is only one randomSeed() then are my 3 variables(X, Y and Time) going to be pretty similar to each other if they are checked for one after another?
Whilst the random() function produces the same sequence if used with the same seed, or no seed, that is not to say that the values returned will be similar to one another in any way
It would be bad practice to call randomSeed() before each call to random(). Think what would happen if the seed value had not changed. One technique that I have used is to have the sketch wait in a loop in setup() until a button is pressed, then to use the value of millis() in randomSeed(). This makes the seed dependant on the time taken to press the button which is not very predictable
For your current purposes using randomSeed() in setup() with an analogRead() value will almost certainly be good enough I would have thought. It is easy enough for you to do some tests by outputting the "random" numbers as comma separated values and importing them into Excel for analysis if you really want to
UKHeliBob:
[..] using randomSeed() in setup() with an analogRead() value will almost certainly be good enough I would have thought.
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:
Thank you all very much. Gave me a lot to think about.
Erik I will have to look at my numbers but in practice I do get one of the servos not moving much if at all every iteration. I need to look at my actual numbers, not the servos since I could just be experiencing an issue somewhere else, but I’m pretty sus about my servos not getting a large difference in coordinate adjustments.
Thanks guys. I know its a silly cat toy, but this was a great catalyst to explore the Random function and how to implement it. <3
Seeding the prng is a bit of a classic problem. On more sophisticated systems you typically have various entropy sources which are in some way combined to generate a "truly random" number which you can then use to seed the prng.
As an example, if a system has a keyboard and mouse (or other user inputs), the timing between interrupts can be a source of entropy.
In practice, unless you're doing cryptographic things, worrying about this level of detail is overkill. People often seed the prng using approaches along the lines of "take the current time, and just use the microseconds part of the timestamp". On a desktop or server type machine that's probably good enough for most typical applications.
Obviously on an Arduino that's a bit more tricky. Boot times are probably very deterministic, so relying on a timestamp is unlikely to produce much randomness.
Practically speaking, a floating input pin is probably as good as it gets!
The outputs of your experiment are probably not as bad as you imagine. The prng output from being seeded with 1014 will be very different if it is seeded with e.g. 1015 or 1013 -- just try it and see.
Furthermore, if you're only seeding the prng once per boot, even if you do hit the same seed value every now and then, it's probably not the end of the world for the purposes of confusing your cats 
tomparkin:
Seeding the prng is a bit of a classic problem. On more sophisticated systems you typically have various entropy sources which are in some way combined to generate a "truly random" number which you can then use to seed the prng.
One needs a prng to seed the prng, so to speak... 
[..] on an Arduino [..] Boot times are probably very deterministic, so relying on a timestamp is unlikely to produce much randomness.
When Serial.println(micros()) is the first in setup(), the result is 8 (µs) on my Uno. Every time. Later on (after ~ 10ms of for() loops with Serial.print()s) there is some difference in the timing!
Practically speaking, a floating input pin is probably as good as it gets!
True. But only if the pin is floating, so not connected to any device with pullup resistors, like mine were when I did the first test... 
it's probably not the end of the world for the purposes of confusing your cats 
I only want the best for my cats. They only want the best. :-p
Erik_Baas:
I only want the best for my cats. They only want the best. :-p
This totallllly isn't my side project while I wait for more parts to show up for my motion sensing spray bottle project to keep my cats off the counter 
tomparkin:
it's probably not the end of the world for the purposes of confusing your cats 
Over engineering is half the fun!
Besides, they love to play with my jumper wires. They might soon figure out how to use pull down resistors on my Analog pins!
Erik_Baas:
One needs a prng to seed the prng, so to speak... 
It's turtles all the way down! 