I'm using an Arduini Nano to control a servo that turns an owl head when triggered by a PIR. To make its movements seem natural, my code generates a random position between 0 and 180. But the owl turns his head way too fast. I'd like to slow the movement down a lot.
The relevant code:
{
if (digitalRead(pirPin) == HIGH)
{ //if the PIR output is HIGH, turn servo
digitalWrite(ledPin, HIGH); //the led visualizes the sensors output pin state
pos = random(180); //generate random value for y-servo // was 180
myservo.write(pos); //y-servo moves to new position
I'm seeing suggestions elsewhere to generate small movements and include a delay between each step. But I'm not sure how to code this. If this were Python, it might look something like this:
for i in range(pos):
myservo.write(pos);
delay(5);
I moved your topic to an appropriate forum category @ghulse.
In the future, please take some time to pick the forum category that best suits the subject of your topic. There is an "About the _____ category" topic at the top of each category that explains its purpose.
/* This code generates a random sweep on a servo after being triggered by a PIR
**/
#include <ESP32Servo.h> // this library needed specifically for the Arduino Nano // for Uno use #include <Servo.h>
Servo myservo; //creates a servo object
int pos = 0; //variable to store servo position
//amount of time we give the sensor to calibrate(10-60 secs according to the datasheet)
int calibrationTime = 30;
//the time when the sensor outputs a low impulse
long unsigned int lowIn;
//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int mypause = 5000;
boolean lockLow = true;
boolean takeLowTime;
int pirPin = 2;//digital pin connected to the PIR's output
int pirPos = 12;//connects to the PIR's 5V pin
int ledPin = 12;
void setup(){
myservo.attach(9);//attaches servo to pin 9
Serial.begin(9600);//begins serial communication
pinMode(pirPin, INPUT);
pinMode(pirPos, OUTPUT);
pinMode(ledPin, OUTPUT); // LED light
digitalWrite(pirPos, HIGH);
//give the sensor time to calibrate
Serial.println("calibrating sensor ");
for(int i = 0; i < calibrationTime; i++){
Serial.print(calibrationTime - i);
Serial.print("-");
delay(800); // gives time for PIR to settle down before reading
}
Serial.println();
Serial.println("done");
while (digitalRead(pirPin) == HIGH) {
delay(500);
Serial.print(".");
}
Serial.print("SENSOR ACTIVE");
}
// newer void loop
void loop()
{
if (digitalRead(pirPin) == HIGH)
{ //if the PIR output is HIGH, turn servo
digitalWrite(ledPin, HIGH); //the led visualizes the sensors output pin state
pos = random(180); //generate random value for y-servo // was 180
myservo.write(pos); //y-servo moves to new position
int delayAmount = random(800, 2800); //generate number between 500 and 2800 // was 500, 2800
delay(delayAmount); // delay by the random amount //to make it go slower, increase the number.
}
else
{
digitalWrite(ledPin, LOW); //the led visualizes the sensors output pin state
myservo.write(90); //move servo to centre // was 90
}
}
Does your new code in Post #6 work? If the motion continues, will new positions be generated? If you want only one new position when movement is detected, you will want to know when movement occurs and when movement stops, something like this... place at beginning of loop() to detect PIR activity...
if (prev_motion_state == LOW && motion_state == HIGH) { // pin state change: LOW -> HIGH
Serial.println("Motion detected");
// move the servo to random position and turn LED on
}
} else if (prev_motion_state == HIGH && motion_state == LOW) { // pin state change: HIGH -> LOW
Serial.println("Motion stopped");
// move servo back to home and turn LED off
}
The code in post #6 is the current code and works perfectly. It goes into Low state until movement is detected by PIR (High) and then it moves servo in random directions for about 15 seconds before going back to Low, awaiting next movement detected by PIR. The only issue is the servo movements are really fast.
I would try this millis() method but the Nano requires a different Servo library. Not sure if the millis() can be tweaked to run with the <ESP32Servo.h> library.
/* This code generates a random sweep on a servo after being triggered by a PIR
**/
#include <ESP32Servo.h> // this library needed specifically for the Arduino Nano // for Uno use #include <Servo.h>
Servo myservo; //creates a servo object
int pos = 0; //variable to store servo position
//amount of time we give the sensor to calibrate(10-60 secs according to the datasheet)
int calibrationTime = 30;
int target = 0;
//the time when the sensor outputs a low impulse
long unsigned int lowIn;
//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int mypause = 5000;
boolean lockLow = true;
boolean takeLowTime;
int pirPin = 2;//digital pin connected to the PIR's output
int pirPos = 12;//connects to the PIR's 5V pin
int ledPin = 12;
void setup(){
myservo.attach(9);//attaches servo to pin 9
Serial.begin(9600);//begins serial communication
pinMode(pirPin, INPUT);
pinMode(pirPos, OUTPUT);
pinMode(ledPin, OUTPUT); // LED light
digitalWrite(pirPos, HIGH);
//give the sensor time to calibrate
Serial.println("calibrating sensor ");
for(int i = 0; i < calibrationTime; i++){
Serial.print(calibrationTime - i);
Serial.print("-");
delay(800); // gives time for PIR to settle down before reading
}
Serial.println();
Serial.println("done");
while (digitalRead(pirPin) == HIGH) {
delay(500);
Serial.print(".");
}
Serial.print("SENSOR ACTIVE");
}
// newer void loop
void loop()
{
if (digitalRead(pirPin) == HIGH)
{ //if the PIR output is HIGH, turn servo
digitalWrite(ledPin, HIGH); //the led visualizes the sensors output pin state
pos = random(180); //generate random value for y-servo // was 180
//myservo.write(pos); //y-servo moves to new position
move (
int target )
{
if ((target - pos) > 0)
servo.write (++pos);
else if ((target - pos) < 0)
servo.write (--pos);
delay (15);
}
int delayAmount = random(800, 2800); //generate number between 500 and 2800 // was 500, 2800
delay(delayAmount); // delay by the random amount //to make it go slower, increase the number.
}
else
{
digitalWrite(ledPin, LOW); //the led visualizes the sensors output pin state
myservo.write(90); //move servo to centre // was 90
}
}
Using library ESP32Servo at version 3.0.5 in folder: /Users/ghulse/Documents/Arduino/libraries/ESP32Servo
exit status 1
Compilation error: expected primary-expression before 'int'
you just copy/pasted the function into the middle of your code instead of copying it as a new function and calling it when appropriate.
the function needs to be called repeatedly with whatever value is intended to be reached. The function moves toward the value with each execution, but ...
... you also modify the value before each execution so it will probably never reach that targer.
look this over
/* This code generates a random sweep on a servo after being triggered by a PIR
**/
# include <ESP32Servo.h> // this library needed specifically for the Arduino Nano // for Uno use #include <Servo.h>
int pirPin = 2;//digital pin connected to the PIR's output
int pirPos = 12;//connects to the PIR's 5V pin
int ledPin = 12;
int calibrationTime = 30;
Servo myservo; //creates a servo object
int pos = 0; //variable to store servo position
//amount of time we give the sensor to calibrate (10-60 secs according to the datasheet)
int target = 0;
//the time when the sensor outputs a low impulse
long unsigned int lowIn;
//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int mypause = 5000;
boolean lockLow = true;
boolean takeLowTime;
// -----------------------------------------------------------------------------
void setup (){
myservo.attach (9);//attaches servo to pin 9
Serial.begin (9600);//begins serial communication
pinMode (pirPin, INPUT);
pinMode (pirPos, OUTPUT);
pinMode (ledPin, OUTPUT); // LED light
digitalWrite (pirPos, HIGH);
//give the sensor time to calibrate
Serial.println ("calibrating sensor ");
for (int i = 0; i < calibrationTime; i++){
Serial.print (calibrationTime - i);
Serial.print ("-");
delay (800); // gives time for PIR to settle down before reading
}
Serial.println ();
Serial.println ("done");
while (digitalRead(pirPin) == HIGH) {
delay (500);
Serial.print (".");
}
Serial.print ("SENSOR ACTIVE");
}
// -----------------------------------------------------------------------------
void move (
int target,
unsigned long dly )
{
static int pos;
if ((target - pos) > 0)
myservo.write (++pos);
else if ((target - pos) < 0)
myservo.write (--pos);
delay (15);
}
// -----------------------------------------------------------------------------
byte pirState;
unsigned long dly;
void loop ()
{
move (pos, dly);
// if the PIR HIGH, select new random value
byte pir = digitalRead(pirPin);
if (pirState != pir) {
pirState = pir; // state change (recognize once)
if (HIGH == pir) {
pos = random (180);
dly = random (800, 2800);
}
else {
pos = 90;
dly = 0;
}
}
}
Your code works great in terms of slowing down the servo. It actually seems like a perfect speed. No more owl whiplash!
Two issues I will try to correct. 1) The servo moves at a slow speed and that's great, but the movements are far and few between. Before it would move several times before going LOW. Now it moves twice only each time it is triggered and not sure how to make it move more. And 2) the LED would turn on when PIR was HIGH and turn off again when in LOW state. With your new code the LED is always on. (This is actually important because I have two leds behind the owl eyes. I like them to light up only when the owl head is moving.)
I added (digitalWrite(ledPin, HIGH) to this part, but not sure where to add the corresponding (digitalWrite(ledPin, LOW); )
That works great. The other thing that doesn't work as expected is that the owl head moves only once per trigger event. It then returns to the start (90) position as it goes back to LOW. As such the "dly = random (800, 2800);" during HIGH doesn't seem to come into play at all. I'd expect it to turn the servo several times before going back to LOW state. Or at least that's what I want to happen. Alternatively, we could just set it to move head x times per cycle in a loop or something like that. But I do like the idea of the head movements happening randomly because it adds to the realism.
yes, looks like the code i posted never replaced the value of 15 in the call to delay() in move() if the dly argument. but a delays of 800 - 2800msec are probably not what you want, you don't want a change of 90 to take 4.2 mins (90 * 2.8. sec)
values need to be much small, < 100.
i alway hope the OP can understand what i post, be able to debug it as well as customize
I don't recall how I came up with the <random (800, 2800);> in the original code but somehow it worked well. The servo moved and there would be a delay of a second or a few seconds before moving again. The servo typically moved 4-5 times while in HIGH state.