Linear Actuator

I am currently working with a Geekduino board from robot geek (same import setting as Arduino Diecimila), a push button from RobotGeek, and an actuonics/firgelli L16-R actuator. My goal is to have the actuator extend to a distance x and come back to 0 while the button is being held. I would want it to start after a half a second, and immediately come back when it reaches x. I would then like the program to reset when the button is released. I have added below links to the components, the actuator is in pin 9 and the button pin 7. I have also included my attempted code, however the code currently only moves the actuator after the button is pressed. Sorry for my ignorance as this is my first attempt at this kind of control.

http://www.actuonix.com/L16_R_Miniature_Linear_Servo_For_RC_p/l16-r.htm

http://www.robotgeek.com/robotgeek-geekduino

http://www.robotgeek.com/robotgeek-sensor-shield

http://www.robotgeek.com/robotGeek-pushbutton

CODE:

//Includes
#include <Servo.h> 

Servo LINEARACTUATOR;
int inPin = 7;  // the pin number for input (for me a push button)
int actPin = 9; 


int out = 1;
int back = 2;
int current;         // Current state of the button
int linearValue = 0; // (LOW is pressed b/c i'm using the pullup resistors)
long millis_held;    // How long the button was held (milliseconds)
long secs_held;      // How long the button was held (seconds)
long prev_secs_held; // How long the button was held in the previous check
byte previous = HIGH;
unsigned long firstTime; // how long since the button was first pressed 


void setup() {
 Serial.begin(9600);         // Use serial for debugging
 LINEARACTUATOR.attach(actPin, 1050, 2000);
 pinMode(actPin, OUTPUT);
 digitalWrite(inPin, HIGH);  // Turn on 20k pullup resistors to simplify switch input
 LINEARACTUATOR.writeMicroseconds(linearValue);
}

void loop() {
 current = digitalRead(inPin);

 // if the button state changes to pressed, remember the start time 
 if (current == LOW && previous == HIGH && (millis() - firstTime) > 200) {
   firstTime = millis();
 }

 millis_held = (millis() - firstTime);
 secs_held = millis_held / 1000;

 // This if statement is a basic debouncing tool, the button must be pushed for at least
 // 100 milliseconds in a row for it to be considered as a push.
 if (millis_held > 50) {

   if (current == LOW && secs_held == out) {
     linearValue = 2000; // Each second the button is held blink the indicator led
   }
   LINEARACTUATOR.writeMicroseconds(linearValue);
   if (current == LOW && secs_held == back){ 
     linearValue = 0;
   }
   LINEARACTUATOR.writeMicroseconds(linearValue);
   // check if the button was released since we last checked
   if (current == HIGH && previous == LOW) {

   }
 }

 previous = current;
 prev_secs_held = secs_held;
}

You could fit 2 microswitches - one at each end of your wanted travel.

there would then be no guesswork in knowing where the actuator is...

ie it's ..

  • at zero
  • travelling to full extension
  • at full extension
  • travelling back to zero

regards

Allan.

Allan,

Thanks for the response, I would prefer to try to complete this without a limit switch. The actuator I have has an internal servo control so should be able to control travel distance with enough accuracy for me. I am just not sure of the code to have the actuator extend when the button is held for ~1 second and then retract when it reaches a certain position (weather that is time controlled or if there is a distance response from the motor I am unsure). Thanks and I hope this clarifies my question.

OK... well, then you'd have to time the actuator response -

run it for 1 sec and measure how far it goes..... scale from that

regards

Allan.

The firgellis that I've used (and it looks like the one you have), can use the RC servo library for simple, repeatable positioning.

lastchancename,

Thanks for the advice, how would I write my code so that the motor would go to (LINEARACTUATOR.wrtite(60)) if the button has been pressed for 1 second, and then (LINEARACTUATOR.wrtie(0)) if the button has been pressed for 2.5 seconds? Am I using the write command correctly in my previous sentence? 60 should give me 1/3 of the total actuation correct?

I apologize for my ignorance but I do not have much programming experience so I think I am just struggling with getting the actuator to move when I ask it to.

Thanks,
Marc

You need a state machine. Probably two. One for the button and one for the motion of the actuator. If you treat the actuator as a simple servo and you don't need to control the speed of its action, then that doesn't need a state machine. So let's start with the button.

Initially, the button isn't pressed. In fact, with the Arduino just powered up, it has no memory of the button ever being pressed in the past. Let's give this state a name. Call it START.

So, what are the triggers that can cause it to leave the IDLE state? The button, of course. When the button is first detected to be pressed, record that time (with the millis() function) and move to the NOT_YET_1_SECOND state.

Now there's two ways to leave this state. If the button is released, that is not held down for a full second, then you go back to START. If the button is still held down while millis() exceeds one second then you can go to the ONE_SECOND state.

In the ONE_SECOND state, we know that if the button is released, we should do the first move (LINEARACTUATOR.wrtite(60)) but until that happens, we do nothing. We might have to wait for the 2.5 second period.

So, after 2.5 seconds, should we continue waiting for the button to be released or should we do the action, which gives the poor user some feedback "you've held that button long enough buddy!" I would always prefer to give the feedback.

But after 2.5 seconds, if the button is still held down, we can't go back to START yet. Because that would just begin the whole cycle again. We need another (empty) state that waits until the button is released.

This makes more sense if you draw it on a piece of paper with circles for the states and arrows showing the state transitions.

#include <Servo.h>
const int ButtonPin = 2; //button on pin 2, grounded when pushed
const int ServoPin = 3; //servo connected to this pin for control
Servo LINEARACTUATOR;
enum States {START, NOT_YET_1_SECOND, ONE_SECOND, STILL_HOLDING_THE_DAMN_BUTTON};
States State; //declare a variable of type States
unsigned long InitialPressTime;
void setup() {
  pinMode(ButtonPin, INPUT_PULLUP); //pullup makes the pin high when button is released
}
void loop() {
  switch(State) {
    case START:
      if(LOW == digitalRead(ButtonPin)) {
        //button has become pressed
       InitialPressTime = millis();
       State = NOT_YET_1_SECOND;
      }
    break;
    case NOT_YET_1_SECOND:
      if(LOW == digitalRead(ButtonPin)) {
        //button is still held down
        if(millis() - InitialPressTime > 1000){
          State =  ONE_SECOND;
        }
      } else {
        //button was released
        State = START;
      }
    break;
    case ONE_SECOND:
      if(LOW == digitalRead(ButtonPin)) {
        //button is still held down - more than 1 second
        if(millis() - InitialPressTime > 2500){
          LINEARACTUATOR.attach(ServoPin);
          LINEARACTUATOR.write(0);
          State =  STILL_HOLDING_THE_DAMN_BUTTON;
        }
      } else {
        //button was released
        LINEARACTUATOR.attach(ServoPin);
        LINEARACTUATOR.write(60);
        State = START;
      }
    break;
    case STILL_HOLDING_THE_DAMN_BUTTON:
      if(LOW == digitalRead(ButtonPin)) {
        //button is still held down - more than 2.5 second
      } else {
        //button was released
        State = START;
      }
    break;
  }
}

MorganS,

Thank you for the very detailed explanation. Is there any way to get the actuator to move when the button is in a pressed state or will it only move when the button is in a released state?

Thanks,
Marc

Of course there is. My program moves the actuator when it is held down more than 2.5 seconds.

What do you want it to do?

MorganS,

I have gotten close to what I need with your advice, what I want it to do is to move to position x if the button is down, then if the button stays down I want it to move back to position 0 after time t. I then do not want it to do anything unless the button has been released and pressed again. With the code below, the actuator moves out when pressed but then does not move back until the button is released.

#include <Servo.h>
const int ButtonPin = 7; //button on pin 2, grounded when pushed
const int ServoPin = 9; //servo connected to this pin for control
Servo LINEARACTUATOR;
unsigned long held;
int previous;

enum States {START, FORWARD};
States State;

void setup() {
  pinMode(ButtonPin, INPUT_PULLUP); //pullup makes the pin high when button is released
  pinMode(ServoPin, OUTPUT);
  LINEARACTUATOR.attach(ServoPin); // attach servo to actPin
  LINEARACTUATOR.writeMicroseconds(1000);
  previous = LOW;

}
void loop() {
  switch(State) {
    case START:
      if(digitalRead(ButtonPin) != previous) {
        //if the button was previously not pressed and is now pressed move forward and start recording time
        held = millis();
        LINEARACTUATOR.writeMicroseconds(1500);
        previous = digitalRead(ButtonPin);
        State = FORWARD;
      }
     break;
     case FORWARD:
     //if the actuator is forward and more than 1000 seconds have passed and the button is still held move back to initial position
      if(LOW == digitalRead(ButtonPin) && held > 1000) {
        LINEARACTUATOR.writeMicroseconds(1000);
        State = START;
      } else {
        State = START;
      }
     break;  
  }
}

This code works better, it goes back and forth, however sometimes it does it when the button is released too, not quite as predictable as i would like.

#include <Servo.h>
const int ButtonPin = 7; //button on pin 2, grounded when pushed
const int ServoPin = 9; //servo connected to this pin for control
Servo LINEARACTUATOR;
unsigned long held;
unsigned long starttime;
int previous = 0;
int current = 0;

enum States {START, FORWARD};
States State;

void setup() {
  pinMode(ButtonPin, INPUT_PULLUP); //pullup makes the pin high when button is released
  pinMode(ServoPin, OUTPUT);
  LINEARACTUATOR.attach(ServoPin); // attach servo to actPin
  LINEARACTUATOR.writeMicroseconds(1000);
  previous = digitalRead(ButtonPin);
  State = START;

}
void loop() {
current = digitalRead(ButtonPin);
  switch(State) {
    case START:
      if(HIGH == current && current != previous) {
        //if the button was previously not pressed and is now pressed move forward and start recording time
        starttime = millis();
        LINEARACTUATOR.writeMicroseconds(1500);
        previous = current;
        State = FORWARD;
     break;
     case FORWARD:
     current = digitalRead(ButtonPin);
     held = millis() - starttime;
     //if the actuator is forward and more than 1000 seconds have passed and the button is still held move back to initial position
      if(current == previous && held > 1000) {
        LINEARACTUATOR.writeMicroseconds(1000);
        previous = current;
        State = START;
      } else {
        previous = current;
        State = FORWARD;
      }
     break; 
      } 
  }
}

Congratulations, you've just re-invented a bad version of Duff's Device. No, it's not a good thing. I'm shocked that the compiler actually lets you do that.

Hit CTRL-T in the Arduino editor. Notice how it screws up your code and the two cases aren't intented the same? It means that your braces ({}) are in the wrong place. You didn't close the if() before starting the second case.

My original state machine removed the need to debounce the switch. The current/previous variables you have added are kind of like adding extra states. I would get rid of them. Or remove the states and just keep a "forward" boolean.

Here is my final working code, thanks for the input and advice.

#include <Servo.h>
const int ButtonPin = 7; //button on pin 2, grounded when pushed
const int ServoPin = 9; //servo connected to this pin for control
Servo LINEARACTUATOR;
int previous = 0;
int current = 0;
unsigned long startTime = 0;
int servoPostion = 0;


void setup() {
   pinMode(ButtonPin, INPUT_PULLUP); //pullup makes the pin high when button is released
   pinMode(ServoPin, OUTPUT);
   LINEARACTUATOR.attach(ServoPin); // attach servo to actPin
   LINEARACTUATOR.writeMicroseconds(1000);
   previous = digitalRead(ButtonPin);
}
void loop() {
   current = digitalRead(ButtonPin);

   if (current == HIGH && current != previous) {
      LINEARACTUATOR.writeMicroseconds(1500);
      startTime = millis();
      servoPostion = 1;
   }
   if (servoPostion == 1 && (millis() - startTime) > 500) {
      LINEARACTUATOR.writeMicroseconds(1000);
      servoPostion = 0;
   }
   previous = current;
}