Linear actuators from Shift Automation - robust performance

# Adjustable limit switches with Arduino

## How to make adjustable limits for a linear actuator

First posted on shiftautomation.com

We previously wrote a step-by-step tutorial on how to use an Arduino to read a linear actuator encoder, this builds on the ideas, circuits and code discussed there.  Once you are able to control a linear actuator with relay, providing some intelligence to the system is the next logical step.  This step-by-step tutorial shows how to easily adjust the limits of travel with a linear actuator with position feedback.

As the actuator moves the encoder gives pulses that the Arduino can count up or down (the quadrature encoder provides direction information).  We can use a button to save the current position as a travel limit.  For example, the button we choose for setting the upper limit will save the position we want for the upper limit of travel.  Likewise for the button we choose for setting the lower position.

The linear actuator is controlled by two buttons (or one SPDT three position switch).

## Circuit with Arduino and linear actuator

Here's a diagram of how the linear actuator and buttons connect to the Arduino. ## Getting into the code

This code makes use of two libraries, a button library and an encoder library.  The libraries make the main code much easier to read and maintain.

``````#include <Button.h> //https://github.com/JChristensen/Button
#include <Encoder.h> //http://www.pjrc.com/teensy/td_libs_Encoder.html``````

We want to use constants for the various pins we are using and set up some constants that the button library uses.

``````const byte pinRelay1 = 7;
const byte pinRelay2 = 6;
const byte pinExtend = 8;
const byte pinRetract = 9;
const byte pinSetLower = 10;
const byte pinSetUpper = 11;
#define PULLUP true        //To keep things simple, we use the Arduino's internal pullup resistor.
#define INVERT true        //Since the pullup resistor will keep the pin high unless the
//switch is closed, this is negative logic, i.e. a high state
//means the button is NOT pressed. (Assuming a normally open switch.)
#define DEBOUNCE_MS 20     //A debounce time of 20 milliseconds usually works well for tactile button switches.``````

And set up the encoder.  As before we will use two of the Arduino interrupt pins.

``````//Set up the linear actuator encoder
//On many of the Arduino boards pins 2 and 3 are interrupt pins
// which provide the best performance of the encoder data.
Encoder myEnc(2, 3); //   avoid using pins with LEDs attached
long oldPosition  = -999;``````

Using the Button library, set up the four buttons; set upper limit, set lower limit, extend actuator and retract actuator.

``````Button btnSetLower(pinSetLower, PULLUP, INVERT, DEBOUNCE_MS);
Button btnSetUpper(pinSetUpper, PULLUP, INVERT, DEBOUNCE_MS);
Button btnExtend(pinExtend, PULLUP, INVERT, DEBOUNCE_MS);
Button btnRetract(pinRetract, PULLUP, INVERT, DEBOUNCE_MS);``````

For this example we are going to start with hard coded limits.  To fully flesh this code out, you'll want to persist the lower limit, upper limit and current position to EEPROM.

``````long topLimit = 500;
long bottomLimit = 0;``````

In your setup() function, the only pins we need to explicitly set are the relay control pins.  Set them as output pins. We have Serial.begin in here to show events in this test code, but you can remove as you see fit.

``````void setup() {
pinMode(pinRelay1, OUTPUT);
pinMode(pinRelay2, OUTPUT);
Serial.begin(9600);
}``````

In the main loop function you can see how using a button library and encoder library make the code much cleaner.  You can look at the library code to see the implementation of the button debounce and state, I am sure you will be satisfied that it is written well.  Checking all four buttons and the encoder takes about 60 microseconds on my UNO.  The code for debouncing is all contained within the libraries, no need to add debouncing code.

``````  btnSetLower.read();

For the upper and lower limits, we want to set the value when the user releases the button.

``````  if (btnSetLower.wasReleased()) {
bottomLimit = currentPosition;
Serial.print("Set lower pos:");
Serial.println(currentPosition);
}

if (btnSetUpper.wasReleased()) {
topLimit = currentPosition;
Serial.print("Set upper pos:");
Serial.println(currentPosition);
}
``````

A bit of logic checking to see if the extend/retract button is pressed, and if so check if you're over the limit.  The exception is when you're currently holding one the of 'set limit' buttons.  Then you can move past the previous set limit.

``````  if (btnRetract.isPressed()) {
if (currentPosition >= bottomLimit || btnSetLower.isPressed()) {
retractActuator();
} else {
stopActuator();
//Serial.print("Position:");Serial.println(currentPosition);
}

}
if (btnExtend.isPressed()) {
if (currentPosition <= topLimit || btnSetUpper.isPressed()) {
extendActuator();
} else {
stopActuator();
//Serial.print("Position:");Serial.println(currentPosition);
}
}

if (!btnRetract.isPressed() && !btnExtend.isPressed()) {
stopActuator();
}
``````

Here's the full code listing:

``````
/*----------------------------------------------------------------------*
* Adjustable linear actuator limits by                                 *
*      Troy Newton (troy@shiftautomation.com)                          *
*                                                                      *
* This code takes input from two buttons to set the upper and lower    *
* limits of travel.  The code assumes position feedback from a Hall    *
* effect quadrature encoder.  Two buttons (or one SPDT switch) are     *
* used as input to control the linear actuator start/stop/direction.   *
*----------------------------------------------------------------------*/
#include <Button.h> //https://github.com/JChristensen/Button
#include <Encoder.h> //http://www.pjrc.com/teensy/td_libs_Encoder.html
const byte pinRelay1 = 7;
const byte pinRelay2 = 6;
const byte pinExtend = 8;
const byte pinRetract = 9;
const byte pinSetLower = 10;
const byte pinSetUpper = 11;
#define PULLUP true        //To keep things simple, we use the Arduino's internal pullup resistor.
#define INVERT true        //Since the pullup resistor will keep the pin high unless the
//switch is closed, this is negative logic, i.e. a high state
//means the button is NOT pressed. (Assuming a normally open switch.)
#define DEBOUNCE_MS 20     //A debounce time of 20 milliseconds usually works well for tactile button switches.

//Set up the linear actuator encoder
//On many of the Arduino boards pins 2 and 3 are interrupt pins
// which provide the best performance of the encoder data.
Encoder myEnc(2, 3); //   avoid using pins with LEDs attached
long oldPosition  = -999;

Button btnSetLower(pinSetLower, PULLUP, INVERT, DEBOUNCE_MS);
Button btnSetUpper(pinSetUpper, PULLUP, INVERT, DEBOUNCE_MS);
Button btnExtend(pinExtend, PULLUP, INVERT, DEBOUNCE_MS);
Button btnRetract(pinRetract, PULLUP, INVERT, DEBOUNCE_MS);

long topLimit = 500;
long bottomLimit = 0;

void setup() {
pinMode(pinRelay1, OUTPUT);
pinMode(pinRelay2, OUTPUT);
Serial.begin(9600);
}

void loop() {

if (btnSetLower.wasReleased()) {
bottomLimit = currentPosition;
Serial.print("Set lower pos:");
Serial.println(currentPosition);
}

if (btnSetUpper.wasReleased()) {
topLimit = currentPosition;
Serial.print("Set upper pos:");
Serial.println(currentPosition);
}

if (btnRetract.isPressed()) {
if (currentPosition >= bottomLimit || btnSetLower.isPressed()) {
retractActuator();
} else {
stopActuator();
}

}
if (btnExtend.isPressed()) {
if (currentPosition <= topLimit || btnSetUpper.isPressed()) {
extendActuator();
} else {
stopActuator();
}
}

if (!btnRetract.isPressed() && !btnExtend.isPressed()) {
stopActuator();
}
}

void extendActuator() {
digitalWrite(pinRelay1, HIGH);
digitalWrite(pinRelay2, LOW);
}

void retractActuator() {
digitalWrite(pinRelay1, LOW);
digitalWrite(pinRelay2, HIGH);
}

void stopActuator() {
digitalWrite(pinRelay1, LOW);
digitalWrite(pinRelay2, LOW);
}

``````