using detach() to cut servos when they aren't needed

Greetings folks!
i am controlling a few servos with my mouse through processing. everythign works fine but i am trying to detach() them when they are not being used as they make an annoying noise which i could do without. unfortunately the following code doesnt work when the detach part is included. anyone spot my error? Many thanks. Danny.

void loop() {

  static int v = 0; // value to be sent to the servo (0-180)
  if ( Serial.available() > 0) {
    xservo.attach(3); //(analog pin 0) for the x servo
    yservo.attach(5);  //(analog pin 1) for the y server
    zservo.attach(6);
    char ch = Serial.read(); // read in a character from the serial port and assign to ch
    switch (ch) { // switch based on the value of ch
      case '0'...'9': // if it's numeric
        v = v * 10 + ch - '0';
        break;
      case 'z':
        zservo.write(v);

        v = 0;
        break;

      case 'x': // if it's x

        xservo.write(v);
        x = v;
        v = 0;
        break;

      case 'y':
        int vM = map(v, 0, 180, 180, 0);
        yservo.write(vM);
        y = v;
        v = 0;
        break;

    }
  }
  else {
    xservo.detach();
    yservo.detach();
    zservo.detach();
  }
}

You don't give the servo's time to reach there goal :wink:

You don't give the servo's time to reach there goal :wink:

Ive got it set up on the processing end so it only sends data when i want it to. so there are big spaces where the arduino is not receiving any data from serial. would this not allow the servos enough time?

No, because the moment you don’t have data and you detach(). But in that <1ms time frame the servo will not reach it’s position. Once you detach the servo has no signal and stops moving, it will not remember to keep on moving to the last received position. It’s very dump and will only try to move to the desired position as long as it’s reminded to do so (which is what the Arduino does as long as it’s attached).

Right, I'm with ya. I'll try and figure out a way around that, thanks.

Remember the time you last received a command (hint! millis()) and only detach some time after that.

Ah sweet, just banged in a delay before the detach and it works! (To delayophobes I shall be implementing a non delay version now);

Might work, not very great if you want to do anything else with the Arduino or want to change the position in the mean time...

gonadgranny:
Ah sweet, just banged in a delay before the detach and it works! (To delayophobes I shall be implementing a non delay version now);

As a card carrying delayophobe, I appreciate the effort. :smiley:

Note that at the most recent annual convention for the Society for the Prevention of Delay Usage, I was voted in as the Grand PooBah. This is a lifetime appointment. :wink:

gonadgranny:
Ah sweet, just banged in a delay before the detach and it works! (To delayophobes I shall be implementing a non delay version now);

I did a non-blocking extension to the Servo library for a project; I’ve attached a bit of it if you are interested. Like you are trying, it detach()s once the motion i complete, but you don’t need delays.

There are two callback functions to indicate a servo started moving and finished its motion. Just use NULL in the constructor if you don’t need them:

.ino

#include "Sweep.h"

void startMoving(int value);
void stoppedMoving(int value);

Sweep servo1(startMoving, stoppedMoving);

void setup() 
{
  Serial.begin(9600);
  servo1.begin(4);
  pinMode(13, OUTPUT);
  Serial.print("Total Servos: ");
  Serial.println(Sweep::getInstanceCount());
}

void loop() 
{
  Sweep::update();
  static unsigned long lastMillis = 0;
  static bool toggle = true;
  if(millis() - lastMillis > 10000)
  {
    Serial.print("Moving to: ");
    Serial.println(toggle? 180:0);
    servo1.moveTo(toggle? 180:0);
    toggle = !toggle;
    lastMillis = millis();
  }
}

void startMoving(int value)
{
  Serial.print("motion started: ");
  Serial.println(value);
  digitalWrite(13, HIGH);
}

void stoppedMoving(int value)
{
  Serial.print("motion completed: ");
  Serial.println(value);
  digitalWrite(13, LOW);
}

.h

#ifndef SWEEP_H
#define SWEEP_H

#include <Servo.h>
#include "Arduino.h"

#define MOTION_SPEED_INTERVAL 20  // milliseconds
#define MAX_SERVO_INSTANCES 4


class Sweep : public Servo {

  public:
    Sweep(void(*_startCallback)(int), void(*_endCallback)(int));
    //~Sweep();
    void begin(int servo);
    void moveTo(uint32_t newPosition);
    static void moveAllTo(uint32_t angle);
    static void update();
    static void update(uint32_t now);
    static int getInstanceCount();
    int getPosition (void) const;
    

  private:
    enum MotionState{
      ARM_STANDBY,
      ARM_MOVING,
    };
    void(*startCallback)(int);
    void(*endCallback)(int);
    static Sweep* instanceAddress;
    static int instanceCount;
    int servoPin;
    MotionState motionState = ARM_MOVING;
    uint32_t lastMotionMillis = 0;
    int currentPosition;
    int targetPosition;
    uint32_t sensedChangedMillis;
    static void setTargetPosition(Sweep* instance, int position);
};

#endif

.cpp

#include "Sweep.h"
#include "Arduino.h"

int Sweep::instanceCount = 0;
Sweep* instances[MAX_SERVO_INSTANCES] = {nullptr};

Sweep::Sweep(void(*_startCallback)(int), void(*_endCallback)(int))
{
  instances[instanceCount++] = this;
  startCallback = _startCallback;
  endCallback = _endCallback;
}

int Sweep::getInstanceCount()
{
  return instanceCount;
}

void Sweep::begin(int servo)
{
  servoPin = servo;
  pinMode(servoPin, OUTPUT);
  attach(servoPin);
  moveTo(0);
  delay(1000);
  detach();
}

void Sweep::setTargetPosition(Sweep* instance, int position)
{
  instance->targetPosition = position;
}

void Sweep::moveTo(uint32_t newPosition)
{
  setTargetPosition(this, newPosition);
}

void Sweep::moveAllTo(uint32_t angle)
{
  for(int i = 0; i < instanceCount; i++)
  {
    setTargetPosition(instances[i], angle);
  }
}

void Sweep::update()
{
  update(millis());
}

void Sweep::update(uint32_t now)
{
  for(int i = 0; i < instanceCount; i++)
  {
    switch (instances[i]->motionState)
    {
      case ARM_MOVING:
        if(now - instances[i]->lastMotionMillis > MOTION_SPEED_INTERVAL)
        {
          if(instances[i]->targetPosition == instances[i]->currentPosition)
          {
            if(instances[i]->attached())
            {
              instances[i]->detach();
              instances[i]->motionState = ARM_STANDBY;
              instances[i]->endCallback(instances[i]->getPosition());
            }
          }
          else
          {
            if(!instances[i]->attached())
            {
              instances[i]->attach(instances[i]->servoPin);
            }
            if(instances[i]->targetPosition > instances[i]->currentPosition)
            {
              instances[i]->currentPosition++;
              instances[i]->write(instances[i]->currentPosition);
            }
            else
            {
              instances[i]->currentPosition--;
              instances[i]->write(instances[i]->currentPosition);
            }
          }
          instances[i]->lastMotionMillis = now;
        }
        break;
      case ARM_STANDBY:
        if(instances[i]->targetPosition != instances[i]->currentPosition)    
        {
          instances[i]->motionState = ARM_MOVING;
          instances[i]->startCallback(instances[i]->getPosition());
        }
      break;
    }
  }
}

int Sweep::getPosition(void) const
{
  return currentPosition;
}
I did a non-blocking extension to the Servo library for a project; I've attached a bit of it if you are interested.  Like you are trying, it detach()s once the motion i complete, but you don't need delays.

There are two callback functions to indicate a servo started moving and finished its motion.  Just use  NULL in the constructor if you don't need them:

Thanks BulldogLowell. Ever helpful as ever!

Lol, most of the time we have to tell people quote tags are not code tags. But seems we need to do the opposite here :smiley:

Code tags are not quote tags :wink:

ha, sorry about that!

BulldogLowell, I’ve just tried compiling that code after creating the files and putting them in the same folder the tabs appear when i open the .ino file but for some reason i get a

fatal error: servo.h : No such file or directory

I tried adding a an #include <Servo.h> to the .ino file and now the error is:

error: ‘nullptr’ was not declared in this scope

Which is odd because thats a library which comes with the IDE by default. Any thoughts? Thanks

gonadgranny:
BulldogLowell, I’ve just tried compiling that code after creating the files and putting them in the same folder the tabs appear when i open the .ino file but for some reason i get a
I tried adding a an #include <Servo.h> to the .ino file and now the error is:
Which is odd because thats a library which comes with the IDE by default. Any thoughts? Thanks

Which version of the IDE are you using?

I compiled and tested using 1.8.2…

you can try to edit this line at the top pf the cpp file"

Sweep* instances[MAX_SERVO_INSTANCES] = {nullptr};

and change it to this:

Sweep* instances[MAX_SERVO_INSTANCES];

I was using an old version and have now updated and it is compiling. :smiley:

gonadgranny:
I was using an old version and have now updated and it is compiling. :smiley:

Also, remember to change the following line in the header file (Sweep.h) if you are using more than four servos:

#define MAX_SERVO_INSTANCES 4

We really should generate a compile error there if user tries to create more than that, but this is just an excerpt from a much bigger class that I did. I just wanted you to have something I know worked.