Hey all! I'm currently using arduino to create an animatronic, and am having an issue. It's supposed to be when the ir sensor is triggered, the servo goes to 160 degrees, than 60, and continues doing that for about 20 seconds. I also have 2 other things that will be on for a couple seconds when the sensor is activated, but that part of the code works great. My current issue is that the servo only moves if the ir sensor is currently triggered, but as mentioned I want it to move for 20 without needing to hold my hand over it. I have tried using the method that uses elapsedmillis to make the servo go for a certain amount of time, but with that method, the servo moves for 21 seconds, stops, and never moves again even if the sensor is triggered. That method also messed up the timing of the 2 other things. Essentially i want the servo to go for 21 seconds when the sensor is triggered using millis. Whatever works works, so if i have to make the servo loop go a million times that's totally fine.
Code:
#include <Servo.h>
int position = 90 ;
boolean forward = false ;
unsigned long ts = millis () ;
#define DELAY 40
int servoPin = 8;
Servo servo;
# define PinSensor 9
# define PinThing1 12
# define PinThing2 13
enum { Off = LOW, On = HIGH};
// -----------------------------------------------------------------------------
void setup ()
{
pinMode (PinSensor, INPUT_PULLUP);
pinMode (PinThing1, OUTPUT);
pinMode (PinThing2, OUTPUT);
digitalWrite (PinThing1, Off);
digitalWrite (PinThing2, Off);
Serial.begin(9600);
servo.attach(8);
}
// -----------------------------------------------------------------------------
struct Timer {
byte pin;
unsigned long duration;
bool on;
};
Timer timers [] = {
{ PinThing1, 5000 },
{ PinThing2, 2000 }
};
#define N_TIMERS (sizeof(timers)/sizeof(Timer))
// -----------------------------------------------------------------------------
void loop ()
{
static unsigned long msecLst;
unsigned long msec = millis ();
// enable timed outputs
if (digitalRead (PinSensor) == LOW) {
if (millis () - ts >= DELAY)
{
ts += DELAY ; // setup timestamp for next time.
if (forward)
{
servo.write (-- position) ; // progress the servo
if (position == 60) // test for reverse
forward = false ;
}
else
{
servo.write (++ position) ; // progress the servo
if (position == 160) // test for reverse
forward = true ;
}
}
msecLst = msec;
for (unsigned n = 0; n < N_TIMERS; n++) {
timers [n].on = true;
digitalWrite (timers [n].pin, On);
}
}
Timer *t = timers;
for (unsigned n = 0; n < N_TIMERS; n++, t++) {
if (t->on && (msec - msecLst) > t->duration) {
digitalWrite (t->pin, Off);
t->on = false;
}
}
}
Thank you all so much for your help! I've only been doing this for a couple of months so don't be suprised if it's an obvious error lol
Hey! tried this and now not only does the servo not work, but the led only stays on if my hand is in front of the sensor. If i use it with elapsed millis the servo works, but again only once, and the led does not work at all. Code:
#include <Servo.h>
int position = 90 ; // state variable
boolean forward = false ; // state variable
unsigned long ts = millis () ; // time accounting.
#define DELAY 40
int servoPin = 8;
Servo servo;
int SensorCounter = 0; // counter for the number of button presses
int SensorState = 0; // current state of the button
int lastSensorState = 0; // previous state of the button
# define PinSensor 9
# define PinThing1 12
# define PinThing2 13
enum { Off = LOW, On = HIGH};
// -----------------------------------------------------------------------------
void setup ()
{
servo.attach(8);
pinMode (PinSensor, INPUT_PULLUP);
pinMode (PinThing1, OUTPUT);
pinMode (PinThing2, OUTPUT);
digitalWrite (PinThing1, Off);
digitalWrite (PinThing2, Off);
Serial.begin(9600);
}
// -----------------------------------------------------------------------------
struct Timer {
byte pin;
unsigned long duration;
bool on;
};
Timer timers [] = {
{ PinThing1, 5000 },
{ PinThing2, 2000 }
};
#define N_TIMERS (sizeof(timers)/sizeof(Timer))
// -----------------------------------------------------------------------------
void loop ()
{ SensorState = digitalRead(PinSensor);
static unsigned long msecLst;
unsigned long msec = millis ();
// enable timed outputs
if (digitalRead (PinSensor) == LOW) {
for (unsigned n = 0; n < N_TIMERS; n++) {
timers [n].on = true;
digitalWrite (timers [n].pin, On);
}
}
Timer *t = timers;
for (unsigned n = 0; n < N_TIMERS; n++, t++) {
if (t->on && (msec - msecLst) > t->duration) {
digitalWrite (t->pin, Off);
t->on = false;
// compare the buttonState to its previous state
if (SensorState != lastSensorState) {
// if the state has changed, increment the counter
if (SensorState == HIGH) {
// if the current state is HIGH then the button went from off to on:
SensorCounter++;
Serial.println("on");
Serial.print("number of button pushes: ");
Serial.println(SensorCounter);
} else {
// if the current state is LOW then the button went from on to off:
Serial.println("off");
}
// save the current state as the last state, for next time through the loop
lastSensorState = SensorState;
if (SensorCounter % 1 == 0) {
if (millis () - ts >= DELAY)
{
ts += DELAY ; // setup timestamp for next time.
if (forward)
{
servo.write (-- position) ; // progress the servo
if (position == 60) // test for reverse
forward = false ;
}
else
{
servo.write (++ position) ; // progress the servo
if (position == 160) // test for reverse
forward = true ;
}
}
}
}
}
}
}
You are saving the current sensor state as the previous sensor state only when the sensor state has changed. That is not what the StateChangeDetection example does.
I'm not entirely sure what you mean by this, Where in the program is this? I want the program to run every time the ir sensor is activated, not just the first time, and from my understanding of it statechangedetection only detects if it changes once, not every time. The servo moves a tiny bit with this program, but barely at all.
Here is the StateChangeDetection example stripped to the bare bones with added comments
const int buttonPin = A3; // the pin that the pushbutton is attached to
int buttonState = HIGH; // current state of the button
int lastButtonState = HIGH; // previous state of the button
void setup()
{
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(115200);
}
void loop()
{
buttonState = digitalRead(buttonPin); //read the current button state
if (buttonState != lastButtonState) //if it has changed since the last time it was read
{
if (buttonState == LOW) //and is now LOW
{
Serial.println("input became LOW");
}
delay(50); //quick and dirty way to debounce
}
lastButtonState = buttonState; //save the current button state ready for the next check for a change
}
Frankly I suggest that you restructure your sketch into a state machine so that the code for each state can be isolated to make it easier to see what is going on
As I understand it the states would be
WAIT_FOR_TRIGGER
MOVE_TO_160_DEGREES //repeat moves and waits for 20 seconds or a fixed count
WAIT_A_BIT //using millis() for non blocking timing
MOVE_TO_60_DEGREES
WAIT_A_BIT //using millis() for non blocking timing
Meanwhile the code can do whatever it likes in loop() as long as it does not block execution of the code. Does that sound right ?
Thanks so much! it works almost perfectly, but i have one question. How would i make the servo go slower? I know with delay you can just increase delay time.
For a quick and dirty way to slow down the servo movement take a look at the Servo Sweep example in the IDE, but there are better, if more complicated, ways to do it