Electronically Actuated Height Adjustable Workbench.

Having more tools than space to use them is a fairly ubiquitous problem.  The idea of a workbench within a workbench (workbench inception) seemed like an a reasonable solution.  Lifting and setting up the saw each time it needed to be used was also discouraging use.  This eliminated that problem, and provided an out-feed table.  It also provided an adequate location for a router lift.

20170101_224040

Above is design concept of the legs, which were constructed first.  The two drawer slides compliment each other in their protection against torsion around a Y axis.  Five legs were utilized in total, which allows for some experimentation in placement of the actuators, and generated further resistance to torsion.

The cap on the leg was found to be the weakest point, and since the linear actuator rests upon it, it is the single most likely aspect of the device to snap under conditions where the actuators are not moving in unison.  This is an acceptable weak point, as it is a (relatively) easy fix.

20170101_224030

The legs are connected by two steel beams, each running the length of the workbench.  The saw rests upon these, and they assist in maintaining the legs at a uniform height.

20170110_205747-e1487479273656.jpg

It became evident with experimentation that the entire base was flexing with use.  A flexible workbench holding a table-saw seemed imprudent.  To avoid manslaughter charges/amputations, a torsion box for the base was constructed.

20170124_191421

Above indicates the torsion box for the work surface itself.  Notice the use of T-nuts to hold the legs in place.  Bolts were used in leu of screws throughout the project.

20170124_213306.jpg

Here is the addition of the work surface lateral to the table saw.  Note the gap, it allows the rack and pinion tracks of the saw to extend left of the saw.

20170219_181047

A router was utilized to cut out the wheel housings.  Dust collection was installed, which will be detailed elsewhere.  The table saw base is adjustable at four points, which allows for leveling relative to the out-feed table.

20170602_184502.jpg

The inner workings.  8 Channel relay.  12V DC, 30 A, 3 channel power supply.  Arduino Mega.  Sainsmart LCD.  Numeric keypad.

20170612_191740

Outside of the control box.

20170612_190958

Interior of the router table.

20170612_191655

Final product.  Some draws may be added along the base, which would prevent stored items from being caught in the legs, and resultant catastrophic failure.  Overall, while some adjustments to the mechanism would be made in the case of future iteration, the concept is functional, and will serve well for future projects.

The code utilized is detailed below:


/*
Works to move three relays at once.

*/
// Libraries
#include
#include
#include
#include
#include
#include

#define I2C_ADDR 0x27 // Define I2C Address where the SainSmart LCD is
#define BACKLIGHT_PIN 3

LiquidCrystal_I2C lcd(I2C_ADDR,2,1,0,4,5,6,7);

// constants won’t change. They’re used here to set pin numbers:
const int button1Pin = 11; // the number of the pushbutton1 pin
const int button2Pin = 12; // the number of the pushbutton2 pin
const int button3Pin = 13; // stop button
const int relay1Pin = 7; // the number of the Realy1 pin
const int relay2Pin = 8; // the number of the Relay2 pin
const int relay3Pin = 3;
const int relay4Pin = 4;
const int relay5Pin = 5;
const int relay6Pin = 6;

const int relay1Interval = 60000;
const int relay2Interval = 60000;

const int blinkDuration = 500; // number of millisecs that Led’s are on – all three leds use this

const int button1Interval = 10;
const int button2Interval = 10;
const int button3Interval = 10;
// number of millisecs between button readings

const byte servoCount = 5;
const int OPEN_ALL = 100;
const int CLOSE_ALL = 99;
const int openPos = 29;
const int closedPos = 101;

static Servo servoList[servoCount];

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
// Define the keymaps. The blank spot (lower left) is the space character.
char numberKeys[ROWS][COLS] = {
{ ‘1’,’2′,’3′ }
,
{ ‘4’,’5′,’6′ }
,
{ ‘7’,’8′,’9′ }
,
{ ‘ ‘,’0′,’#’ }
};

byte rowPins[ROWS] = { 37,39,41,43 };
byte colPins[COLS] = { 31,33,35 };

// Create two new keypads, one is a number pad and the other is a letter pad.
Keypad numpad( makeKeymap(numberKeys), rowPins, colPins, sizeof(rowPins), sizeof(colPins) );

const byte ledPin = 13;

// variables will change:
int button1State = 0; // variable for reading the pushbutton status
int button2State = 0; // variable for reading the pushbutton status
int button3State = 0;
int relay1State = 0;
int relay2State = 0;

byte relay1 = LOW;
byte relay2 = LOW;
byte button1 = LOW;
byte button2 = LOW;
byte button3 = LOW;

const int sensorPin = 0; // select the input pin for the potentiometer(doesn’t matter here)
int sensorValue = 0; // variable to store the value coming from the sensor(doesn’t matter here)

unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousrelay1Millis = 0; // will store last time the relay was updated
unsigned long previousrelay2Millis = 0;
unsigned long previousbutton1Millis = 0;
unsigned long previousbutton2Millis = 0;
unsigned long previousbutton3Millis = 0; // time when button press last checked

void setup() {

//start serial connection
Serial.begin(9600);
Serial.println(“Workbench_actuator_stoppable”); // so we know what sketch is running

// initialize the pushbutton pin as an input:
pinMode(button1Pin, INPUT);
pinMode(button2Pin, INPUT);
pinMode(button3Pin, INPUT);
// initialize the relay pin as an output:
pinMode(relay1Pin, OUTPUT);
pinMode(relay2Pin, OUTPUT);
pinMode(relay3Pin, OUTPUT);
pinMode(relay4Pin, OUTPUT);
pinMode(relay5Pin, OUTPUT);
pinMode(relay6Pin, OUTPUT);

Serial.begin(9600);
pinMode(31, INPUT);
pinMode(33, INPUT);
pinMode(35, INPUT);
pinMode(37, INPUT);
pinMode(39, INPUT);
pinMode(41, INPUT);
pinMode(43, INPUT);

pinMode(A0, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A2, OUTPUT);
pinMode(A3, OUTPUT);

numpad.begin( makeKeymap(numberKeys) );
numpad.addEventListener(keypadEvent_num); // Add an event listener.
numpad.setHoldTime(500); // Default is 1000mS

servoList[1].attach(A0);
servoList[2].attach(A1);
servoList[3].attach(A2);
servoList[4].attach(A3);

// setServoState(CLOSE_ALL); //reset all servos

lcd.begin (20, 4);

// Switch on the backlight

lcd.setBacklightPin(3,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home();

}

char key;
static char virtKey = NO_KEY; // Stores the last virtual key press. (Alpha keys only)
static char physKey = NO_KEY; // Stores the last physical key press. (Alpha keys only)
static char buildStr[12];
static byte buildCount;
static byte pressCount;
static byte kpadState;

void loop(){

currentMillis = millis(); // capture the latest value of millis()
// this is equivalent to noting the time from a clock

// read the state of the pushbutton values:

updateRelay_1State();
updateRelay2State();
readbutton1State();
readbutton2State();
switchrelay1();
readbutton3State();
key = numpad.getKey( );

// updatebutton2State();
// updatebutton3State();
}

void updateRelay_1State() {

if ( relay1 == LOW) {
//
if (currentMillis – previousrelay1Millis >= relay1Interval) {
// time is up, so change the state to HIGH
relay1State = LOW;
// and save the time when we made the change
previousrelay1Millis += relay1Interval;
// NOTE: The previous line could alternatively be
// previousOnBoardLedMillis = currentMillis
// which is the style used in the BlinkWithoutDelay example sketch
// Adding on the interval is a better way to ensure that succesive periods are identical

}
}
else { // i.e. if onBoardLedState is HIGH

// if the Led is on, we must wait for the duration to expire before turning it off
if (currentMillis – previousrelay1Millis >= relay1Interval) {
// time is up, so change the state to LOW
relay1State = LOW;
// and save the time when we made the change
previousrelay1Millis += relay1Interval;
}
}
}

void switchrelay1() {
// this is the code that actually switches the things on and off

digitalWrite(relay1Pin, relay1State);
digitalWrite(relay2Pin, relay2State);
digitalWrite(relay3Pin, relay1State);
digitalWrite(relay4Pin, relay2State);
digitalWrite(relay5Pin, relay1State);
digitalWrite(relay6Pin, relay2State);
digitalWrite(button1Pin, button1State);
digitalWrite(button2Pin, button2State);

}

void readbutton1State() {

// this only reads the button state after the button interval has elapsed
// this avoids multiple flashes if the button bounces
// every time the button is pressed it changes buttonLed_State causing the Led to go on or off
// Notice that there is no need to synchronize this use of millis() with the flashing Leds

if (millis() – previousbutton1Millis >= button1Interval) {

if (digitalRead(button1Pin) == HIGH) {
relay1State = HIGH;
relay2State = LOW;

previousbutton1Millis += button1Interval;
}
// else if (digitalRead(button1Pin) == LOW) {
// relay1State = LOW;
// }
}
}

void updateRelay2State() {

if ( relay2 == LOW) {
//
if (currentMillis – previousrelay1Millis >= relay2Interval) {
// time is up, so change the state to HIGH
relay2State = LOW;

// and save the time when we made the change
previousrelay2Millis += relay2Interval;
// NOTE: The previous line could alternatively be
// previousOnBoardLedMillis = currentMillis
// which is the style used in the BlinkWithoutDelay example sketch
// Adding on the interval is a better way to ensure that succesive periods are identical

}
}
else { // i.e. if onBoardLedState is HIGH

// if the Led is on, we must wait for the duration to expire before turning it off
if (currentMillis – previousrelay2Millis >= relay2Interval) {
// time is up, so change the state to LOW
relay2State = LOW;
// and save the time when we made the change
previousrelay2Millis += relay2Interval;
}
}
}

void readbutton2State() {

// this only reads the button state after the button interval has elapsed
// this avoids multiple flashes if the button bounces
// every time the button is pressed it changes buttonLed_State causing the Led to go on or off
// Notice that there is no need to synchronize this use of millis() with the flashing Leds

if (millis() – previousbutton2Millis >= button2Interval) {

if (digitalRead(button2Pin) == HIGH) {
relay2State = HIGH;
relay1State = LOW;

previousbutton2Millis += button2Interval;
}
// else if (digitalRead(button1Pin) == LOW) {
// relay1State = LOW;
// }
}
}

void readbutton3State() {

// this only reads the button state after the button interval has elapsed
// this avoids multiple flashes if the button bounces
// every time the button is pressed it changes buttonLed_State causing the Led to go on or off
// Notice that there is no need to synchronize this use of millis() with the flashing Leds

if (millis() – previousbutton3Millis >= button3Interval) {

if (digitalRead(button3Pin) == LOW) {
relay2State = LOW;
relay1State = LOW;

previousbutton3Millis += button3Interval;
}
// else if (digitalRead(button1Pin) == LOW) {
// relay1State = LOW;
// }
}
}
void setServoState(char number) {
int thisServ = 0;
int val;
boolean ignoreAll = false;
switch (number){
case ‘1’:
val = 1;
break;
case ‘2’:
val = 2;
break;
case ‘3’:
val = 3;
break;
case ‘4’:
val = 4;
break;
case ‘5’:
val = 5;
break;
case ‘6’:
val = 6;
break;
case ‘7’:
val = 7;
case ‘8’:
val = 8;
case ‘9’:
ignoreAll = true;
break;
default:
val=number;
break;
}
if(!ignoreAll){
for(thisServ = 0; thisServ < servoCount; thisServ += 1){
int newPos = 0;
if(thisServ == val or val == OPEN_ALL){
newPos = openPos;
}
else {
newPos = closedPos;
}
servoList[thisServ].write(newPos);
}
}

}

void keypadEvent_num( KeypadEvent key ) {
// in here when using number keypad
kpadState = numpad.getState();
swOnState( key );

} // end numbers keypad events

void swOnState( char key ) {
Serial.print(“key = “);
Serial.print(key);
Serial.println();
lcd.setCursor (0, 0);
lcd.print(” Gate: “);
lcd.print(key);
lcd.print(” “);
lcd.setCursor (0, 1);
lcd.print(” “);

switch( kpadState ) {
case PRESSED:

if (isdigit(key) || key == ‘ ‘ || key == ‘.’){
setServoState(key);
}
if (key == ‘5’){
setServoState(3), setServoState(4);}

if (key == ‘#’) {
Serial.print(“CLOSE ALL”);
Serial.println();
lcd.setCursor (0, 1);
lcd.print(” All Closed “);
lcd.setCursor (0, 0);
lcd.print(” “);
setServoState(CLOSE_ALL);
}
if (key == ‘ ‘) {
Serial.print(“OPEN ALL”);
Serial.println();
lcd.setCursor (0, 1);
lcd.print(” All Open “);
lcd.setCursor (0, 0);
lcd.print(” “);
setServoState(OPEN_ALL);
}
break;
case HOLD:
if (key == ‘#’) { // Toggle between keymaps.
}
else { // Some key other than ‘#’ was pressed.

}
break;

case RELEASED:
break;
} // end switch-case
}// end switch on state function