Trying to change servo sweep speed based on number of people in a room

Hey, this is my first post on this site. (sorry if it is in the wrong place)
I am trying to make a project where a set of two LDRs work together along with lasers to count the number of people entering and leaving a room.

What I then want to do is control the constant sweep speed of a servo to make it change based on the number of people currently in the room.

For example, if there are 0 people in the room, the servo moves at a slow speed. If there is 1 person in the room, the servo increases in speed slightly, and so on, up to a limit of 10 people.

At the moment, I am rather new to Arduino so my coding knowledge is limited. My servo currently changes speed once a certain number is reached (I am testing whether it changes after 5 people at this time), but it is causing my sensors to not respond until the servo has completed it's sweep. I want my sensors and servo to run simultaneously so that they don't cause each other to wait until an action is done.

I have tried to replace the delays with some form of millis but I do not currently understand how to implement it in a way to make the servo work. The best I could achieve was the servo rapidly vibrating until (and only when) a LDR is triggered.

Below is my code before playing with millis because that what closer to working than the mess I wrote after!

#include <Servo.h>

Servo myservo;
int sensA; 
int sensB; 
int people = 4;
int thresh;
int pos = 0;

void setup() {
  pinMode(4, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(13, OUTPUT);
  Serial.begin(9600);
  myservo.attach(9);
}

void loop(){

 {
  sensA = analogRead(A0);                    
  sensB= analogRead(A1);
  thresh = 300;
  digitalWrite(4, HIGH);
  digitalWrite(2, HIGH);
  people = constrain (people, 0, 10);
  
  if(sensA<thresh && sensB>thresh)
    {
    digitalWrite(13, HIGH);
    people=people+1;
    delay(1000);
    } 
    else{
      people=people;
      digitalWrite(13, LOW);
  }

  if(sensA>thresh && sensB<thresh)
    {
    digitalWrite(13, HIGH);
    people=people-1;
    delay(1000);
    } 
    else{
      people=people;
      digitalWrite(13, LOW);
  }
 
 Serial.print("sensA is :  ");                          
 Serial.print(sensA   ); 
 Serial.print("      sensB is :  ");
 Serial.print(sensB    );
 Serial.print("     people :  "); 
 Serial.print(people); 
 Serial.print("  thresh val : ");
 Serial.println(thresh);
 }

  if(people <= 5) {
      for (pos = 0; pos <= 180; pos += 1) { 
    // in steps of 1 degree
    myservo.write(pos);              
    delay(50);                     
  }
  for (pos = 180; pos >= 0; pos -= 1) { 
    myservo.write(pos);              
    delay(50);
  }
  } 
  else {
  if(people >= 5); {
      for (pos = 0; pos <= 180; pos += 1) { 
    // in steps of 1 degree
    myservo.write(pos);              
    delay(15);                     
  }
  for (pos = 180; pos >= 0; pos -= 1) { 
    myservo.write(pos);              
    delay(15);
}
}
  }}

Please help! :slight_smile:

I have tried to replace the delays with some form of millis but I do not currently understand how to implement it in a way to make the servo work.

It is just like an LED blinking, only instead to turning on and off an LED, each time slice you get you move the servo one small bit. To speed things up the number of milliseconds that must elapse between each entry of the state machine is changed.

Have you come across this http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html
you might find it simpler to follow.

You're going to have to use millis() and there are plenty of good tutorials about. Try
https://forum.arduino.cc/index.php?topic=503368.0
https://forum.arduino.cc/index.php?topic=223286.0

Then please show us your best attempt at using them and we'll start from there. I doubt if anyone is just going to want to rewrite your code for you without any input by you.

BTW thanks for working out how to post code correctly. You're already well ahead of many people here ;).

Steve

Thanks for the input guys!

I have tried to understand millis using some of the info you gave me, but I am really struggling!

At this moment in time , I have managed to make my servo move without interruption from the LDRs. But I am now having loads of issues with the people counter. Currently the counter doesn't actually count anymore, and it only serial.writes a few times when the laser connection is broken.

So one thing has made progress and one has messed up a load :confused:
The way the two lasers and LDRs used to work was when one was activated, it would add +1 to the people count, then delay for a few seconds to allow the person to fully move through the lasers.

By trying to replace the delay with millis, I have been unsuccessful in replicating the way this worked.
I must be clearly doing something horribly wrong to cause my working code to not work at all?

I haven't even got round to trying to make my servo change speeds based on the number of people yet, because I clearly don't understand how to make the millis work!! haha

Any advice on getting my code back on track?

Thanks! :slight_smile:

#include <Servo.h>

const int servoMinDegrees = 0;
const int servoMaxDegrees = 180;

Servo myservo;

int servoPosition = 90;     // the current angle of the servo - starting at 90.
int servoSlowInterval = 80; // millisecs between servo moves
int servoFastInterval = 10;
int servoInterval = servoSlowInterval; // initial millisecs between servo moves
int servoDegrees = 2;       // amount servo moves at each step 
                           //    will be changed to negative value for movement in the other direction
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop
unsigned long previousServoMillis = 0; // the time when the servo was last moved
long interval = 3000;
long activatedMillis = 0;

int sensA; 
int sensB; 
int people = 0;
int thresh;


void setup() {
  Serial.begin(9600);
  Serial.println("---Starting People Detection---");
  pinMode(4, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(13, OUTPUT);
  
  myservo.attach(9);
  myservo.write(servoPosition); // sets the initial position
}

void loop(){

  currentMillis = millis();   // capture the latest value of millis()

  peoplecounter();
  servoSweep();
 
}
void peoplecounter(){
 {
  sensA = analogRead(A0);                    
  sensB= analogRead(A1);
  thresh = 900;
  digitalWrite(4, HIGH);
  digitalWrite(2, HIGH);
  people = constrain (people, 0, 10);
  
  if(sensA<thresh && sensB>thresh)
    {
      activatedMillis = currentMillis;
      if(currentMillis - activatedMillis > interval) {
    digitalWrite(13, HIGH);
    people=people+1;
    
    } 
    else{
      people=people;
      digitalWrite(13, LOW);
  }

  if(sensA>thresh && sensB<thresh)
    {
        activatedMillis = currentMillis;
      if(currentMillis - activatedMillis > interval) {
    digitalWrite(13, HIGH);
    people=people-1;
    
    } 
    else{
      people=people;
      digitalWrite(13, LOW);
  }}
 
 Serial.print("sensA is :  ");                          
 Serial.print(sensA   ); 
 Serial.print("      sensB is :  ");
 Serial.print(sensB    );
 Serial.print("     people :  "); 
 Serial.print(people); 
 Serial.print("  thresh val : ");
 Serial.println(thresh);
 }

}
}
void servoSweep() {

     // this is similar to the servo sweep example except that it uses millis() rather than delay()

     // nothing happens unless the interval has expired
     // the value of currentMillis was set in loop()
 if (currentMillis - previousServoMillis >= servoInterval) {
       // its time for another move
   previousServoMillis += servoInterval;
   
   servoPosition = servoPosition + servoDegrees; // servoDegrees might be negative

   if (servoPosition <= servoMinDegrees) {
         // when the servo gets to its minimum position change the interval to change the speed
      if (servoInterval == 80) {
        servoInterval = 10;
      }
      else {
       servoInterval = 80;
      }
   }
   if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees))  {
         // if the servo is at either extreme change the sign of the degrees to make it move the other way
     servoDegrees = - servoDegrees; // reverse direction
         // and update the position to ensure it is within range
     servoPosition = servoPosition + servoDegrees; 
   }
       // make the servo move to the next position
   myservo.write(servoPosition);
       // and record the time when the move happened
 }
}

I don't think this does any harm but it messes up the formatting

void peoplecounter() {
  {

Here's one thing that definitely can't work

        activatedMillis = currentMillis;
      if(currentMillis - activatedMillis > interval)

You set activatedMillis equal to currentMillis and then immediately check to see if they're different. Since nothing has changed either of them between the first and second lines they're not different.

You have to set the time that you've done something immediately after you've done it and then not reset it every time you go round the loop.

I can't quite work out if there's a similar problem in servosweep(). But it looks wrong because you add servoInterval to previousServoMillis and THEN you go and change the value of servoInterval. And the comment on the last line says "and record the time when the move happened" but there's no code that does that.

Steve

Hey guys,

I feel I am nearly done.

To clarify what i am trying to do again:

The aim is to create two tripwires using lasers and LDRs which work together to count the number of people entering and leaving a room. The Arduino records this number as people.

What I want to achieve is to make a constantly moving servo to adapt it's sweeping speed dependent on the number of people in the room.

Example: 0 people in the room = servo speed minimum
1 person in the room = servo speed increases
5 people in the room = servo speed increased greatly
10 people in the room = servo speed max
3 people in the room = servo speed decreased greatly
(the numbers of people would obviously not be recorded like that. It would be sequential)

This is what I am struggling with. I would use delays to easily change the servo speed but I am avoiding delays so that the whole setup operates constantly.
Does anyone have tips on how to make the servo speed change based on people number?

Thank you. :slight_smile:

#include <Servo.h>

const int servoMinDegrees = 0;
const int servoMaxDegrees = 180;

Servo myservo;

int servoPosition = 90;     // the current angle of the servo - starting at 90.
int servoInterval = 80; // initial millisecs between servo moves
int servoDegrees = 2;       // amount servo moves at each step 
                           //    will be changed to negative value for movement in the other direction
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop
unsigned long previousServoMillis = 0; // the time when the servo was last moved
long interval = 3000;
long activatedMillis = 0;

int sensA; 
int sensB; 
int people = 0;
int thresh = 0;


void setup() {
  Serial.begin(9600);
  Serial.println("---Starting People Detection---");
  pinMode(4, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(13, OUTPUT);
  thresh = 800;
  myservo.attach(9);
  myservo.write(servoPosition); // sets the initial position
}

void loop(){

  currentMillis = millis();   // capture the latest value of millis()

  peoplecounter();
  servoSweep();
  
  sensA = analogRead(A0);                    
  sensB= analogRead(A1);
  
  digitalWrite(4, HIGH);
  digitalWrite(2, HIGH);
  people = constrain (people, 0, 10);
  
 Serial.print("current    ");
 Serial.print(currentMillis);
 Serial.print("   activated   ");
 Serial.print(activatedMillis); 
 Serial.print("   Servo Speed   ");
 Serial.print(servoDegrees);
 Serial.print("   sensA is :  ");                          
 Serial.print(sensA   ); 
 Serial.print("      sensB is :  ");
 Serial.print(sensB    );
 Serial.print("     people :  "); 
 Serial.print(people); 
 Serial.print("  thresh val : ");
 Serial.println(thresh);
}
void peoplecounter(){
 

  
  if(sensA<thresh && sensB>thresh)
    {
        activatedMillis = currentMillis;
        people=people+1; 
        digitalWrite(13, HIGH);
        thresh = 0;
    }
    else{
      if(currentMillis - activatedMillis >= interval)
      {thresh = 800;
      }
     else{
      people=people;
      digitalWrite(13, LOW);
   
     }
    } 
    
        
  if(sensA>thresh && sensB<thresh)
    {
        activatedMillis = currentMillis;
        people=people-1; 
        digitalWrite(13, HIGH);
        thresh = 0;
    }
    else{
      if(currentMillis - activatedMillis >= interval)
      {thresh = 800;
      }
     else{
      people=people;
      digitalWrite(13, LOW);
   
     }
    } 
  }


void servoSweep() {
 
 if (currentMillis - previousServoMillis >= servoInterval) {
       // its time for another move
   previousServoMillis += servoInterval;
   servoPosition = servoPosition + servoDegrees; // servoDegrees might be negative

 if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees)) {
    servoDegrees = - servoDegrees; // reverse direction
         // and update the position to ensure it is within range
     servoPosition = servoPosition + servoDegrees; 
   }
       // make the servo move to the next position
   myservo.write(servoPosition);
 }
  }

I can’t see any evidence of you using a state machine in this code. That is the whole point here. A state machine would have a totally different structure. The top level is the loop function. Here you look to see if a task is due by compairing the current time with the time the task should be run, then when the time is right you call the function that does that task. You have two tasks, moving the servo and detecting people.
The moving the servo is simple as it only has one state, you enter it and the servo is moved one fixed small amount. The last thing that function does is to set the variable that determines when that task runs again. This time will be a function of the number of people in the room, you can use a map function call to give you a number that changes with the number of people.

The counting people is more complex, that has a number of states and you should use a state variable to keep track of what to do next. So the counting people task would first look for a breaking beam, then set when to look at the beam next, this is the equivalent of your delay, because no further progress is made until that time expires. But the time is used by the moving servo task. Then when you detect the beam is not broken, look for the other beam, this is the next stage or state of that task. Finally a delay, that is don’t do that task for a bit, and finally the increment of the number of people. The state variable lets you keep track of where you are with that task.

I've only had a quick look, but in your case isn't it as simple as reducing "servoInterval" as the current value of "people" increases ?

Steve

slipstick:
I've only had a quick look, but in your case isn't it as simple as reducing "servoInterval" as the current value of "people" increases ?

Steve

I tried that but no matter what I do my servo barely changes speed :confused:
No matter if it is 200 or 20.
Not sure if I know how to do this effectively.

No matter what I do, I cannot seem to change the speed of the servo while using this code.
The servo code I am using is from an example that I have adapted. And the original example code allows for the servo to change it's speed. I don't think I have changed anything major to remove this ability, but in my own code, changing the servoInterval does absolutely nothing!

I am stumped :frowning:

Here is the code I am borrowing from for reference (edited down to show only the servo stuff)

#include <Servo.h>

const int servoPin = 9; // the pin number for the servo signal

const int servoMinDegrees = 20; // the limits to servo movement
const int servoMaxDegrees = 150;

Servo myservo;  // create servo object to control a servo 

int servoPosition = 90;     // the current angle of the servo - starting at 90.
int servoSlowInterval = 80; // millisecs between servo moves
int servoFastInterval = 10;
int servoInterval = servoSlowInterval; // initial millisecs between servo moves
int servoDegrees = 2;       // amount servo moves at each step

unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()

unsigned long previousServoMillis = 0; // the time when the servo was last moved

//========

void setup() {

 myservo.write(servoPosition); // sets the initial position
 myservo.attach(servoPin);

}

//=======

void loop() {

     // Notice that none of the action happens in loop() apart from reading millis()
     //   it just calls the functions that have the action code

 currentMillis = millis();   // capture the latest value of millis()

 servoSweep();

}

//========

void servoSweep() {

     // this is similar to the servo sweep example except that it uses millis() rather than delay()

     // nothing happens unless the interval has expired
     // the value of currentMillis was set in loop()
 
 if (currentMillis - previousServoMillis >= servoInterval) {
       // its time for another move
   previousServoMillis += servoInterval;
   
   servoPosition = servoPosition + servoDegrees; // servoDegrees might be negative

   if (servoPosition <= servoMinDegrees) {
         // when the servo gets to its minimum position change the interval to change the speed
      if (servoInterval == servoSlowInterval) {
        servoInterval = servoFastInterval;
      }
      else {
       servoInterval = servoSlowInterval;
      }
   }
   if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees))  {
         // if the servo is at either extreme change the sign of the degrees to make it move the other way
     servoDegrees = - servoDegrees; // reverse direction
         // and update the position to ensure it is within range
     servoPosition = servoPosition + servoDegrees; 
   }
       // make the servo move to the next position
   myservo.write(servoPosition);
       // and record the time when the move happened
 }
}

//=====END

No matter what I do, I cannot seem to change the speed of the servo while using this code.

Get rid of all the serial print crap in the loop function, the delay that causes is much longer than the delays you are causing to the servo update with the state machine implementation.

Grumpy_Mike:
Get rid of all the serial print crap in the loop function, the delay that causes is much longer than the delays you are causing to the servo update with the state machine implementation.

Thank you! as you can tell, I am quite new to all this.
After moving the serial code to a separate file, my servo now correctly changes speed depending on the number of people in the room!

Thank you so much!