I'm trying to make a circuit which will make a dogs eyes light up in red, play a barking noise and make the jaw move up and down.
I have a working code for the first two items but I am struggling to do a code for a servo to move up and down multiple times. It's easy to get it to move once, but to go up and down x 4 without using a delay and messing up the rest of the code I am getting stuck on.
I've tried various methods and cannot yet seem to get something that works well so some advice would be very much appreciated.
Code is here:
//libraries and pin declarations
#include <SoftwareSerial.h>
#include <Servo.h>
#include <MD_YX5300.h>
SoftwareSerial mySerial(2,3);
MD_YX5300 mp3(mySerial);
#define LEDLeft 10
#define LEDRight 11
#define PIR 13
#define up 0
#define down 180
//servo declaration
Servo servo;
//values and variables to time the left LED
bool LEDLeftBool = false;
bool LEDLeftOn = false;
unsigned long LEDLeftPressTime = 0;
const int LEDLeftOnTime = 3000;
//values and variables to time the right LED
bool LEDRightBool = false;
bool LEDRightOn = false;
unsigned long LEDRightPressTime = 0;
const int LEDRightOnTime = 8000;
//values and variables to time the MP3 player
bool MP3Bool = false;
bool MP3On = false;
unsigned long MP3PressTime = 0;
const int MP3OnTime = 5000;
//values and variables to time the servo
bool servoBool = false;
bool servoOn = false;
unsigned long servoPressTime = 0;
const int servoOnTime = 1000;
void setup() {
pinMode(LEDLeft, OUTPUT);
pinMode(LEDRight, OUTPUT);
pinMode(PIR, INPUT);
mySerial.begin(9600);
mp3.begin();
mp3.setSynchronous(true);
mp3.volume(30);
servo.attach(6);
}
void loop() {
//left eye LED code
if (digitalRead(PIR) == HIGH && LEDLeftOn == false) {
LEDLeftPressTime = millis();
LEDLeftOn = true;
LEDLeftBool = true;
digitalWrite(LEDLeft, HIGH);
}
//Right eye LED code
else if (digitalRead(PIR) == HIGH && LEDRightOn == false) {
LEDRightPressTime = millis();
LEDRightOn = true;
LEDRightBool = true;
digitalWrite(LEDRight, HIGH);
}
//MP3 Code
else if (digitalRead(PIR) == HIGH && MP3On == false) {
MP3PressTime = millis();
MP3On = true;
MP3Bool = true;
mp3.volume(30);
mp3.playTrack(2);
}
//Servo Code
else if (digitalRead(PIR) == HIGH && servoOn == false) {
servoPressTime = millis();
servoOn = true;
servoBool = true;
}
//NEED A SERVO FUNCTION HERE WHICH GOES FROM 90 to 120 3 or 4 times
//if statements to end the various items
if (LEDLeftOn == true && millis() - LEDLeftPressTime >= LEDLeftOnTime) {
digitalWrite(LEDLeft, LOW);
LEDLeftOn = false;
}
else if (LEDRightOn == true && millis() - LEDRightPressTime >= LEDRightOnTime) {
digitalWrite(LEDRight, LOW);
LEDRightOn = false;
}
else if (MP3On == true && millis() - MP3PressTime >= MP3OnTime) {
mp3.volume(0);
MP3On = false;
}
else if (servoOn == true && millis() - servoPressTime >= servoOnTime) {
servo.write(90); //This part of the servo code can revert back to a standard position
servoOn = false;
}}
Use a counter and a 'switch' statement to perform different operations at each interval:
if (servoOn && millis() - servoPressTime >= servoOnTime)
{
// Time to move the servo
switch (ServoMoveCount)
{
case 0:
case 2:
case 4:
case 6:
servo.write(up);
ServoMoveCount++;
break;
case 1:
case 3:
case 5:
case 7:
servo.write(down);
ServoMoveCount++;
break;
default:
ServoMoveCount = 0;
servoOn = false;
break;
}
}
Note: I would simplify the code by only checking the PIR sensor once:
// Detect motion
if (digitalRead(PIR) == HIGH)
{
//left eye LED code
if (!LEDLeftOn)
{
LEDLeftOn = true;
digitalWrite(LEDLeft, HIGH);
LEDLeftPressTime = millis();
}
//Right eye LED code
if (!LEDRightOn)
{
LEDRightOn = true;
digitalWrite(LEDRight, HIGH);
LEDRightPressTime = millis();
}
//MP3 Code
if (!MP3On)
{
MP3PressTime = millis();
MP3On = true;
mp3.volume(30);
mp3.playTrack(2);
}
//Servo Code
if (!servoOn)
{
servoPressTime = millis();
servoOn = true;
}
} // End of motion sensed
Note: The "xxxBool" variables don't seem to be used for anything so you can remove them.
Press left button when Servo at Pos 0 -> Sweeps four times between 0 and 90
Press left button when Servo at Pos 120 -> Goes to 0 and then sweeps four times between 0 and 90
It is just an example to show how to use a state machine and enums: Let the compiler make sure that each enum has its separate value. This way you can add or insert enums like needed without taking care of the values in your code.
No. That is not how 'case' labels works. They are correct as I wrote them. Cases 0, 2, 4, and 6 all do the same thing. Cases 1, 3, 5, and 7 all do the same thing.
it will become much easier to understand your code if you add a verbal description of the wanted code-behaviour.
I write my interpretation of what you have posted:
if a PIR-senor detects an object = PIR-senor-output goes high do three things:
1: switch on left LED (left eye of the dog)
2: switch on right LED (right eye of the dog)
3: start to playback the barking sound
after that
switch of left LED after a certain time
switch of right LED after a certain time (which can be different from the ontime of the left LED
stop playback after a certain time (which can be different from the ontime of the left / right LED )
in parallel to playing back the MP3-barking sound sweep a servo 4 times from 90 to 120 degrees and back 90-120--90-120--90-120--90-120
As you want to do things in parallel you need non-blocking timing.
You have a certain sequence of things that shall happen. This can be solved by using a state-machine as already suggested.
What is not yet explained is the basic behaviour of a state-machine.
a state-machine is code that does quickly jump-in - jump-out of a function.
The execution-time of the function is always short.
jump-in - do a small step - jump-out
This quickly jumping in/out enables to do mutliple things "in parallel".
The second important thing about state-machines is:
that always only a part of the code gets executed.
This code-execution is mutually exclusive. It is either part "A" or part"B" or part "C" that gets executed per one call of the function.
This realises the quickly jump-in / jump-out ALL repeating and ALL looping is done by loop().
If the description above is correct I would divide your code into three functional parts
switch_on_off_LEDS
playBarking
sweepServo
void loop() {
switch_on_off_LEDS(); // quickly jump in/out to enable execution of the line below
playBarking(); // quickly jump in/out to enable execution of the line below
sweepServo(); // quickly jump in/out to enable looping through loop()
}
all three functions will work in a quickly jump_in / jump_out manner
working through its own small state-machine