The Smart Finger-Box Joint Jig

As promised in this post I will explain what I used to make my own automatic Finger-Box Joint for my BOSCH PTS 10 table saw but it can be also used on all other table saws with the due modifications.

Please note that this page is still under construction as I will update this article a little bit at the time. Please also note that the table saw is a dangerous tool and the usage of this jig is under your responsibility. Please be sure to have the necessary knowledge before trying to this at home.

Let’s start with the 3D printed hardware: you can download the 3D step files on my Thingiverse page, here. These are the parts you should 3D print with their relative quantities:

  • 0113 – FRAME GRP – 608 BEARING SUPPORT –> 2 PCS
  • 0108 – FRAME GRP – BEARING SUPPORT BACK –> 2 PCS
  • 0155 – LIMIT PUSH –> 1 PC
  • 0115 – FRAME GRP – CARRYING BLOCK –> 1 PC
  • 0116 – FRAME GRP – CARRYING BLOCK CAP –> 1 PC
  • 0118 – FRAME GRP – GEAR, LEAD SCREW –> 1 PC
  • 0119 – FRAME GRP – GEAR, MOTOR –> 1 PC
  • 0121 – FRAME GRP – LINEAR BEARING –> 3 PCS
  • 0123 – FRAME GRP – MOTOR SUPPORT –> 1 PC
  • 0134 – FRAME GRP – LCD SHIELD COVER –> 1 PC
  • 0138 – FRAME GRP – BUTTON –> 5 PCS

In addition to those parts you might want to use the zero play guide that I designed some time ago, but this will depend on the dimensions of your table saw. If you want to use my design, you’ll find the parts and explanation here.

Concerning the electronics of this project below you’ll find the material I used:

  • Arduino MEGA (ELEGOO) – Limit Switch
  • 24 V 10 A power supply
  • TB6560 STEPPER DRIVER
  • Small breadboard
  • Nema23 motor

I uploaded some time ago a video showing how the homing procedure works and a test of the LCD shield I used. You can find them here. The TB6560 has been set up with the following settings:

  • SW1 OFF
  • SW2 ON
  • SW3 ON
  • S1 OFF
  • S2 OFF
  • S3 ON
  • S4 ON
  • S5 OFF
  • S6 OFF

In this set-up the stepper driver allows to microstep the motor. The Nema23 has 200 steps per revolution, but this new stepping setup it will have 200 / (1/8) — > 1600 microsteps per revolution.

Here is my Arduino code, V3.0.: it is working fine but needs some refinements as some features are still missing.

/***************************************************************************************
    Name    : Ardu Box Jointer
    Author  : Max MakeVerse
    Created : 19/01/2019
    Last Modified: 26/03/2020
 ***************************************************************************************/
    const float verSion = 3.0;

/*    
 *     to be done or verified:
 *     homing procedure
 *     limit switch
 *     LED to illuminate the blade
 *     
 */

/***************************************************************************************
 *  Hardware:
 *    - TB6560 with the following settings
 *        SW1 OFF
 *        SW2 ON 
 *        SW3 ON
 *        S1 OFF
 *        S2 OFF
 *        S3 ON
 *        S4 ON
 *        S5 OFF
 *        S6 OFF
 ***************************************************************************************/

// -----------------------------------------------------------------------------------------------------------------------------------------------------
// Stepping variables
// -----------------------------------------------------------------------------------------------------------------------------------------------------


const int STEP_PIN = 30;   // clk+
const int Enable_PIN = 32; // en+
const int DIR_PIN = 34;    //cw+
const int inLed = 13;
const int lSwitch = 53;


int velocity;                 //  this is set by the user and serves to calculate the following variable.
                              //  value from 1 to 10 then the corresponding actual value is calculated


int accelRate;             //  value from 1 to 10 then the corresponding actual value is calculated

int homingVelocity = 10;
int homingAccel = 1;
float homingSteps = 1;         // mills before checking
float curPosition = 0;
int adjustmentVelocity = 8;
int minStepDelay = 55;        //  max velocity of the stepper motor
int maxMovingStepDelay = 2500;      //  min velocity of the stepper motor
float bigMovement =5;
float smallMovement =0.1;
int minAccelCycles = 20;      //
int maxAccelCycles = 500;     ////'int movementDone;




// -----------------------------------------------------------------------------------------------------------------------------------------------------
// Finger joint variables
// -----------------------------------------------------------------------------------------------------------------------------------------------------

float numberOfFingers = 10;   // has to be even !!! this value is the number of fingers and slots
float kerf = 2.8;
float lumberWidth = 91;
float Overlap = 0.5;
float tolerance = 0.1;   // this is the tolerance between the plain slot and the cut slot

float avanzamentoStd = kerf - Overlap;
float dimSlot = (lumberWidth) / (numberOfFingers);    // ideal dimension
float slotTolerance = dimSlot + tolerance;
int NumeroCicliInSlot = slotTolerance / (kerf - Overlap);

long jobTimer;

// -----------------------------------------------------------------------------------------------------------------------------------------------------
// Creates 3 CUSTOM CHARACTERS for the menu display
// -----------------------------------------------------------------------------------------------------------------------------------------------------
byte downArrow[8] = {     // 8 is the number of arrays
  0b00100, //   *         // you can make your own custom characters at https://maxpromer.github.io/LCD-Character-Creator/
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b10101, // * * *
  0b01110, //  ***
  0b00100  //   *
};

byte upArrow[8] = {
  0b00100, //   *
  0b01110, //  ***
  0b10101, // * * *
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b00100  //   *
};

byte menuCursor[8] = {
  B01000, //  *
  B00100, //   *
  B00010, //    *
  B00001, //     *
  B00010, //    *
  B00100, //   *
  B01000, //  *
  B00000  //
};


// this is a special character to display the status of the homing procedure
byte loadingCursor[8] = {
  B01010,
  B10101,
  B01010,
  B10101,
  B01010,
  B10101,
  B01010,
  B10101
};

// -----------------------------------------------------------------------------------------------------------------------------------------------------
// include the LCD libraries
// -----------------------------------------------------------------------------------------------------------------------------------------------------
#include <Wire.h>
#include <LiquidCrystal.h>

// Setting the LCD shields pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


// Navigation button variables
int readKey;
// Menu control variables
String menuItems[] = {"Ardu BOX JOINT", "Fingers", "Kerf", "Width", "Overlap", "Tolerance", "Results", "Home procedure", "Start Cut", "Move Axis", "Correction", "Slow Move"};
int menuPage = 0;
int maxMenuPages = 12;           // round(((sizeof(menuItems) / sizeof(String)) / 2) + .5);
int cursorPosition = 1;



// Stepper Motor Travel Variables

long TravelX;                       // this is used to store the X value
long maxTravelX = 1350;             // Default maximum travel in [mm]
int move_finished = 1;              // used to check if the movement is completed
long initial_homing = -1;           // this is the initial movement of the stepper used during the homing procedure
                                    // where the minus symbol represents the direction of the rotation (translation)
int homing = 0;                     // if the homing procedure has been done the value will be 1 and the menu will skip to
                                    // ask agai if you want to home. You'll be able just to press left and go back to the main
                                    // page of the menu.
double correction = 1.21;   
double gearReduction = 0.5 /correction;          //1.28 direction of the rotation should be take into account
float mmPerRev = 1.5;               //  5 mm x
int motorStepsPerRev = 200;         // this is the spec of the stepper motor
float microStepsSetting  = 0.125;       // 1/8 --> this is to be set on the TB6560 
float stepsPerRev =  motorStepsPerRev / microStepsSetting;  // for each revolution this number of steps are needed



// -----------------------------------------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {

  // Initializes serial communication
  Serial.begin(9600);

//Serial.println("beginning");
//movy();
  
  pinMode(STEP_PIN, OUTPUT);
  pinMode(Enable_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(inLed, OUTPUT);
  pinMode(lSwitch, INPUT);
  digitalWrite(STEP_PIN, LOW);
  digitalWrite(Enable_PIN, HIGH); //When device turned on the motor has NOT holding power. The holdig power is given after homing

  // -----------------------------------------------------------------------------------
  // call HOMING PROCEDURE function
  // -----------------------------------------------------------------------------------
  //homingProcedure();                  // this function initializes the homing procedure once the Arduino board is powered on


  // Initializes and clears the LCD screen
  lcd.begin(16, 2);
  lcd.clear();

  // Creates the byte for the 3 custom characters
  lcd.createChar(0, menuCursor);
  lcd.createChar(1, upArrow);
  lcd.createChar(2, downArrow);
  lcd.createChar(3, loadingCursor);


  // -----------------------------------------------------------------------------------
  // Display Credits
  // -----------------------------------------------------------------------------------
  startMenu();
}

void loop() {

  mainMenuDraw();
  drawCursor();
  operateMainMenu();
}


void startMenu() {
  unsigned long stART = millis();
  unsigned long duration = 5000;
  printOnLCDA("ArduBox V" + String(verSion), "Have Fun! [Max]");
  delay(1000);
}

void mainMenuDraw() {

  // -----------------------------------------------------------------------------------------------------------------------------------------------------
  //  This function will generate the 2 menu items that can fit on the screen. They will change as you scroll through your menu.
  //  Up and down arrows will indicate your current menu position.
  // -----------------------------------------------------------------------------------------------------------------------------------------------------


  //Serial.print(menuPage);

  printOnLCDA(menuItems[menuPage], menuItems[menuPage + 1]);

  //  lcd.clear();
  //  lcd.setCursor(1, 0);
  //  lcd.print(menuItems[menuPage]);
  //  lcd.setCursor(1, 1);
  //  lcd.print(menuItems[menuPage + 1]);

  if (menuPage == 0) {
    lcd.setCursor(15, 1);
    lcd.write(byte(2));
  } else if (menuPage > 0 and menuPage < maxMenuPages) {
    lcd.setCursor(15, 1);
    lcd.write(byte(2));
    lcd.setCursor(15, 0);
    lcd.write(byte(1));
  } else if (menuPage == maxMenuPages) {
    lcd.setCursor(15, 0);
    lcd.write(byte(1));
  }
}


void drawCursor() {
  // When called, this function will erase the current cursor and redraw it based on the cursorPosition and menuPage variables.
  for (int x = 0; x < 2; x++) {     // Erases current cursor
    lcd.setCursor(0, x);
    lcd.print(" ");
  }

  // The menu is set up to be progressive (menuPage 0 = Item 1 & Item 2, menuPage 1 = Item 2 & Item 3, menuPage 2 = Item 3 & Item 4), so
  // in order to determine where the cursor should be you need to see if you are at an odd or even menu page and an odd or even cursor position.
  if (menuPage % 2 == 0) {
    if (cursorPosition % 2 == 0) {  // If the menu page is even and the cursor position is even that means the cursor should be on line 1
      lcd.setCursor(0, 0);
      lcd.write(byte(0));
    }
    if (cursorPosition % 2 != 0) {  // If the menu page is even and the cursor position is odd that means the cursor should be on line 2
      lcd.setCursor(0, 1);
      lcd.write(byte(0));
    }
  }
  if (menuPage % 2 != 0) {
    if (cursorPosition % 2 == 0) {  // If the menu page is odd and the cursor position is even that means the cursor should be on line 2
      lcd.setCursor(0, 1);
      lcd.write(byte(0));
    }
    if (cursorPosition % 2 != 0) {  // If the menu page is odd and the cursor position is odd that means the cursor should be on line 1
      lcd.setCursor(0, 0);
      lcd.write(byte(0));
    }
  }
}


void operateMainMenu() {
  int activeButton = 0;
  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 0: // When button returns as 0 there is no action taken
        break;
      case 1:  // This case will execute if the "forward" button is pressed
        button = 0;
        switch (cursorPosition) { // The case that is selected here is dependent on which menu page you are on and where the cursor is.
          case 0:
            menuItem1();  // not used as the first line is used for the logo
            break;
          case 1:
            menuItem2();  // number of fingers setting
            break;
          case 2:
            menuItem3();  // kerf settings
            break;
          case 3:
            menuItem4();  // lumber width settings
            break;
          case 4:
            menuItem5();  // overlap setting
            break;
          case 5:
            menuItem6();  // tolerance setting
            break;
          case 6:
            menuItem7();  // homing procedure
            break;
          case 7:
            menuItem8();  // homing procedure
            break;
          case 8:         
            menuItem9();
            break;
          case 9:         
            menuItem10();// move axis
            break;
          case 10:
            menuItem11(); //"Correction"
          case 11:
            menuItem12(); //"slow moving"  
        }
        activeButton = 1;
        mainMenuDraw();
        drawCursor();
        break;
      case 2:
        button = 0;
        if (menuPage == 0) {
          cursorPosition = cursorPosition - 1;
          cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));
        }
        if (menuPage % 2 == 0 and cursorPosition % 2 == 0) {
          menuPage = menuPage - 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        if (menuPage % 2 != 0 and cursorPosition % 2 != 0) {
          menuPage = menuPage - 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        cursorPosition = cursorPosition - 1;
        cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));

        mainMenuDraw();
        drawCursor();
        activeButton = 1;
        break;
      case 3:
        button = 0;
        if (menuPage % 2 == 0 and cursorPosition % 2 != 0) {
          menuPage = menuPage + 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        if (menuPage % 2 != 0 and cursorPosition % 2 == 0) {
          menuPage = menuPage + 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        cursorPosition = cursorPosition + 1;
        cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));
        mainMenuDraw();
        drawCursor();
        activeButton = 1;
        break;
    }
  }
}


int evaluateButton(int x) {
  // This function is called whenever a button press is evaluated. The LCD shield works by observing a voltage drop across the buttons all hooked up to A0.
  int result = 0;
  if (x < 50) {
    result = 1; // right
  } else if (x < 195) {
    result = 2; // up
  } else if (x < 380) {
    result = 3; // down
  } else if (x < 790) {
    result = 4; // left
  }
  return result;
}


void drawInstructions() {
  // If there are common usage instructions on more than 1 of your menu items you can call this function from the sub
  // menus to make things a little more simplified. If you don't have common instructions or verbage on multiple menus
  // I would just delete this void. You must also delete the drawInstructions()function calls from your sub menu functions.

  lcd.setCursor(0, 1); // Set cursor to the bottom line
  lcd.print("Use ");
  lcd.print(byte(1)); // Up arrow
  lcd.print("/");
  lcd.print(byte(2)); // Down arrow
  lcd.print(" buttons");
}



void eraseLine(int row) {
  lcd.setCursor(0, row);                        // Start of row
  lcd.print("                    ");            // 20 spaces
  lcd.setCursor(0, row);                        // Start of row
}



void menuItem1() { // Function executes when you select the 2nd item from main menu
  int activeButton = 0;
  lcd.clear();
}


void menuItem2() { // Function executes when you select the 2nd item from main menu
  int activeButton = 0;


  printOnLCD("Fingers Needed:", String(numberOfFingers));
  //  lcd.clear();
  //  lcd.setCursor(0, 0);
  //  lcd.print("Fingers Needed:");
  //  lcd.setCursor(0, 1);
  //  lcd.print(numberOfFingers);


  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);

    lcd.cursor();                                 // Turn on blinking cursor
    lcd.blink();                                  // Turn on blinking cursor

    lcd.noBlink();                                // Turn off blinking curosr
    lcd.noCursor();                               // Turn off blinking curosr

    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:                               // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
      case 2:                               // This case will execute if the "up" button is pressed
        numberOfFingers++;
        eraseLine(1);                         // Erases line 1
        lcd.print(numberOfFingers);
        break;
      case 3:                               // This case will execute if the "down" button is pressed
        numberOfFingers--;
        eraseLine(1);                       // Erases line 1
        lcd.print(numberOfFingers);
        break;
    }
  }
}

void menuItem3() {                          // Function executes when you select the 3rd item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Kerf size:");
  lcd.setCursor(0, 1);
  lcd.print(kerf);

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
      case 2: // This case will execute if the "up" button is pressed
        kerf = kerf + 0.05;
        eraseLine(1);                      // Erases line 1
        lcd.print(kerf);
        break;
      case 3: // This case will execute if the "down" button is pressed
        kerf = kerf - 0.05;
        eraseLine(1);                      // Erases line 1
        lcd.print(kerf);
        break;
    }
  }
}

void menuItem4() { // Function executes when you select the 3rd item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Lumber width:");
  lcd.setCursor(0, 1);
  lcd.print(lumberWidth);

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
      case 2: // This case will execute if the "up" button is pressed
        lumberWidth = lumberWidth + 0.1;
        eraseLine(1);                      // Erases line 1
        lcd.print(lumberWidth);
        break;
      case 3: // This case will execute if the "down" button is pressed
        lumberWidth = lumberWidth - 0.1;
        eraseLine(1);                      // Erases line 1
        lcd.print(lumberWidth);
        break;
    }
  }
}

void menuItem11() { //correction parameter
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Correction:");
  lcd.setCursor(0, 1);
  lcd.print(correction);

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
      case 2: // This case will execute if the "up" button is pressed
        correction = correction + 0.01;
        eraseLine(1);                      // Erases line 1
        lcd.print(correction);
        break;
      case 3: // This case will execute if the "down" button is pressed
        correction = correction - 0.01;
        eraseLine(1);                      // Erases line 1
        lcd.print(correction);
        break;
    }
  }

  gearReduction = 0.5 /correction;
}

void menuItem5() { // Function executes when you select the 5th item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Cut Overlap:");
  lcd.setCursor(0, 1);
  lcd.print(Overlap);

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
      case 2: // This case will execute if the "up" button is pressed
        Overlap++;
        eraseLine(1);                      // Erases line 1
        lcd.print(Overlap);
        break;
      case 3: // This case will execute if the "down" button is pressed
        Overlap--;
        eraseLine(1);                      // Erases line 1
        lcd.print(Overlap);
        break;
    }
  }
}

void menuItem6() { // Function executes when you select the 6th item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Tolerance:");
  lcd.setCursor(0, 1);
  lcd.print(tolerance);

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
      case 2: // This case will execute if the "up" button is pressed
        tolerance = tolerance + 0.01;
        eraseLine(1);                      // Erases line 1
        lcd.print(tolerance);
        break;
      case 3: // This case will execute if the "down" button is pressed
        tolerance = tolerance - 0.01;
        eraseLine(1);                      // Erases line 1
        lcd.print(tolerance);
        break;
    }
  }
}

void menuItem7() {  // This sub menu is useful to display the results of the settings

  int activeButton = 0;

  lcd.clear();

  // calculate again the main parameters
  avanzamentoStd = kerf - Overlap;
  dimSlot = lumberWidth / numberOfFingers;
  Serial.println(dimSlot);
  slotTolerance = dimSlot + tolerance;
  Serial.println(slotTolerance);
  NumeroCicliInSlot = slotTolerance / (kerf - Overlap);

  lcd.setCursor(0, 0);
  lcd.print("SlotTol-> " + String(slotTolerance));
  lcd.setCursor(0, 1);
  lcd.print("CuttCycles->" + String(NumeroCicliInSlot));


  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }

}

void menuItem8() { // this submenu calls the HOMING PROCEDURE function
  int activeButton = 0;

  if (homing == 1) {
    homing = 0;

    // printOnLCD(alpha, beta);
    printOnLCD("Homing done", "");

    while (activeButton == 0) {
      int button;
      readKey = analogRead(0);
      if (readKey < 790) {
        delay(100);
        readKey = analogRead(0);
      }
      button = evaluateButton(readKey);
      switch (button) {
        case 4:  //  back button is pressed and go back to the main menu
          button = 0;
          activeButton = 1;
          break;
      }
    }
  } else if (homing == 0) {

    String yn = "No";
    printOnLCD("Start Homing?", yn);

    //  Reading the button value in order to switch from yes to no and viceversa

    while (activeButton == 0) {
      int button;
      readKey = analogRead(0);

      if (readKey < 790) {
        delay(100);
        readKey = analogRead(0);
      }

      //  Button evaluation function

      button = evaluateButton(readKey);

      switch (button) {
        case 1:                       // Right button is pressed
          if (yn == "Yes") {
            homingPro();      // If yes the HOMING function is called
          } else if (yn == "No") {
            button = 0;             // If no, acts same as if the left button was called
            activeButton = 1;
          }
          break;
        case 2:                                 // Up button
          if (yn == "No") {
            yn = "Yes";
            eraseLine(1);                      // Erases line 1
            lcd.print(yn);
          } else if (yn == "Yes") {
            yn = "No";
            eraseLine(1);                      // Erases line 1
            lcd.print(yn);
          }
          break;
        case 3:                                // Down button
          if (yn == "No") {
            yn = "Yes";
            eraseLine(1);                      // Erases line 1
            lcd.print(yn);
          } else if (yn == "Yes") {
            yn = "No";
            eraseLine(1);                      // Erases line 1
            lcd.print(yn);
          }
          break;
        case 4:                                // Left button, go back
          button = 0;
          activeButton = 1;
          break;
      }
    }
  }
}

void menuItem9() { // Function executes when you select the 8th item from main menu
  int activeButton = 0;

  printOnLCD("Cutting proced.", "activated!");

  delay(2000);

  // drawInstructions(); // could be useful
  // if the right button is pressed, then start the cutting procedure
  // new submenu with special instructions and button functionalities

  cuttingProcedure();


  printOnLCD("Cutting Done!", "- - - - - -");

  moveUntil(-kerf,10);

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}


void menuItem10(){
  
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("bigMovement:");
  lcd.setCursor(0, 1);
  lcd.print(bigMovement);

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
      case 2: // This case will execute if the "up" button is pressed
        bigMovement = bigMovement + 5;
        eraseLine(1);                      // Erases line 1
        lcd.print(bigMovement);
        break;
      case 3: // This case will execute if the "down" button is pressed
        bigMovement = bigMovement - 5;
        eraseLine(1);                      // Erases line 1
        lcd.print(bigMovement);
        break;
    }
  }

  manualMovement(bigMovement,10);

  activeButton = 0;
  
  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}


void menuItem12(){
  
  int activeButton = 0;

  manualMovement(0.5,5);

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}

void manualMovement(float displacement, int velocity){

  printOnLCD("Adjust w motor", "then press right-");
  
  int  activeButton = 0;

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(30);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);

    switch (button) {
      case 1:                                     //  Right button is pressed
        activeButton = 1;                         //  Means the user accepts the current position and wants to proceed to next step
        break;
      case 2:                                     // Up button
        moveBy(displacement, velocity,0);           //  increment the position of the motor
        break;
      case 3:                                     // Down button
        moveBy(-displacement, velocity,0);          //  Decrement the position of the motor     
        break;
    }
  }
  
}

void cuttingProcedure() {

  eraseLine(1);
  eraseLine(2);

  jobTimer = millis();

  // -------------------------------------------------------------------------------
  //  calculate again the parameters
  // -------------------------------------------------------------------------------

  avanzamentoStd = kerf - Overlap;
  dimSlot = lumberWidth / numberOfFingers;
  slotTolerance = dimSlot + tolerance;
  NumeroCicliInSlot = slotTolerance / (kerf - Overlap);                       // Number of cuts required per slot
  float initial_position;                                                     // will be used in the cutting procedure: it's the first cut of each slot

  //  float finalCut = pinWidth - k;                // The final cut has its right hand edge on the right hand side of the slot
  //  int c = ceil(pinWidth / k);                   

  //  float inc = finalCut / (c - 1);               // Increment to be added for each cut - except the last



  // -------------------------------------------------------------------------------
  //  Set up your pieces and clamp them
  // -------------------------------------------------------------------------------
  
  Serial.println(lumberWidth);
  Serial.println(numberOfFingers);
  displayNextStep("Slot dimension", String(dimSlot));
  
  printOnLCD("Clamp the piece", "then press right-"); 

  int activeButton = 0;

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 1:                                     //  Right button is pressed
        activeButton = 1;                         //  Proceed to next step
        break;
      case 4:                                     //  Left button, abort the cutting procedure
        button = 0;
        activeButton = 1;
        // go back function or reset has to be added
        break;
    }
  }
  // -------------------------------------------------------------------------------
  //  gantry position adjustment
  // -------------------------------------------------------------------------------


  manualMovement(0.1, adjustmentVelocity);

  
/*
 *  From the perspective of the user, the blade is now set touching the right hand 
 *  of the piece of  wood. However this is not the zero position of the blade. 
 *  Therefore the sled has to be moved away from the blade
 */

  displayNextStep("Ready to go?!", "Press right");
  
  displayNextStep("Remove sled", "Press right");

/*  Now that the sled piece is clear off the blade, the gantry can move to the right so that
 *  The right side of the blade is at the same level of the right side of the blade itself.
 *  A moveBy is therefore necessary
 */

  moveBy(kerf, 10,0);

  displayNextStep("Blade is now set", "Let's go");
  
  Serial.println("The blade is now set to zero and the cutting procedure can finally start. Current position is " + String(curPosition) + " mm");
  Serial.println("--------------------------------------------");
  
/*  Now everything is set in order to proceed with the cutting procedure
 *  which is being called into a for cycle
 */
  
    for (int i = 1; i <= numberOfFingers; i++) {                 //  cycle to be performed for each finger
      if ( (i % 2) == 0) {
        // number of the slot is even and doesn't have to be cut according to this program
        // jump to the next odd numbered slot
        Serial.println("--------------------------------------------");
        Serial.println("--------------------------------------------");
        Serial.println("This is slot number " + String(i) + " and it is even, therefore the gantry has to move to the next finger");
//        Serial.println("movement done is = " + String(movementDone));
        Serial.println("--------------------------------------------");
      } else {
        // number of the slot is odd and has to be cut according to this program
        displayNextStep("Cutting slot", "#" + String(i));
        Serial.println("--------------------------------------------");
        Serial.println("--------------------------------------------");
        Serial.println("This is slot number " + String(i) + " and it is odd, therefore the cutting procedure can begin:");
//        Serial.println("movement done is = " + String(movementDone));
        Serial.println("Cutting procedure initiated for the slot number " + String(i));
        cutSlot(i);         // brings you into the cutting routine     
        Serial.println("--------------------------------------------");
      }

    }

  displayNextStep("Cutting done", "press right");

}


void cutSlot(int i) {

/*  
 *   For the first slot to be cut the blade is positioned actually with its right end
 *   on the A position, while its left end is on the B position. This is the first cut to be done
 */

  Serial.println("The current position of the right side of the piece is: " + String(curPosition) + ";");

  float x;

  if (i == 1){
    Serial.println("--------------------------------------------");
    Serial.println("Since this is the first cut of the first slot, the gantry has to go back of about the Overlap");
//    Serial.println("movement done is = " + String(movementDone));
    moveBy((tolerance /2 ), 9,0);
  }else{
    Serial.println("--------------------------------------------");
    Serial.println("Movement towards the first side of the " + String(i) + " slot");
    //Serial.println("movement done is = " + String(movementDone));
    x = (tolerance / 2 - (i - 1) * dimSlot);
    Serial.println("Move until " + String(x));
    moveUntil(x, 10);  
  }
 
  displayNextStep("Cut the piece", "then press right");

  Serial.println("Moving towards last slot ");
  x= - tolerance / 2 + kerf - (i) * dimSlot;
  
  Serial.println("Move until " + String(x));
  moveUntil(x, 10);
  
  displayNextStep("cut the piece", "press right");

  /*  the boundaries are now cut and it is now time to cut all the leftover
   *  remaining between the points Bi and Di
   */
  Serial.println("---------------------------------------------");
  Serial.println("The current position of the right side of the piece is now: " + String(curPosition));

  Serial.println("Cutting the leftover");

  float limitPosition = -(i - 1)*dimSlot + (tolerance / 2);

  float nextPosition = curPosition + kerf - Overlap;
  
  while (nextPosition  <= (limitPosition- kerf)){
    
    moveUntil(nextPosition,10);
    
    //moveBy(+ kerf - Overlap, 10,0); 
      
    displayNextStep("cut the piece", "press right");  

    nextPosition = curPosition + kerf - Overlap;
  }

  Serial.println("Next step would be " + String(nextPosition) + " mm, and it is beyond the boundary limit of ");
  Serial.println(String(limitPosition) + " mm and " + String(limitPosition + kerf));
  Serial.println("Last cut of the slot");

  moveUntil(limitPosition - kerf + Overlap, 10);
  displayNextStep("cut the piece", "press right");  

  Serial.println("Leftover is cut");
  Serial.println("---------------------------------------------");
  
}


void printOnLCD(String alpha, String beta) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(alpha);
  lcd.setCursor(0, 1);
  lcd.print(beta);
  Serial.println("----------------------------------------------");
  Serial.println("Console message: " + alpha);
  Serial.println(beta);
  Serial.println("----------------------------------------------");
}

void printOnLCDA(String alpha, String beta) {
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print(alpha);
  lcd.setCursor(1, 1);
  lcd.print(beta);
  Serial.println("Console message: " + alpha);
  Serial.println(beta);
}

void displayNextStep(String alpha, String beta) {
  printOnLCD(alpha, beta);

  int activeButton = 0;

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);

    switch (button) {
      case 1:                                     // Right button is pressed
        activeButton = 1;                         //  Proceed to next step
        break;
    }
  }
}



void moveBy(float mmDisplacement, int vel, int ignore) {
  
  //Serial.println("movement done = " + String(movementDone));

  Serial.println("current position is " + String(curPosition));
  
  int direct;

  if (mmDisplacement == 0){
    direct = 1;
    Serial.println("no displacement has to be done");
  }else {
    direct = mmDisplacement / abs(mmDisplacement);
  }
  
  


  Serial.println("the direction is " + String(direct));

  long steps = (mmDisplacement / (mmPerRev * gearReduction)) * stepsPerRev;

  steps = abs(steps);

  Serial.println("the steps are " + String(steps));  
  
  Serial.println("Number of steps to do a displacement of " + String(mmDisplacement) + " mm is: " + String(steps));
  
  digitalWrite(Enable_PIN, LOW);
  // relative stepping
  int stpDelay;

  if (direct >= 0) {
    Serial.println("Movement of " + String(mmDisplacement) + " to the LEFT");
    digitalWrite(Enable_PIN, LOW);
    digitalWrite(DIR_PIN, HIGH);
  } else if (direct < 0) {
    Serial.println("Movement of " + String(mmDisplacement) + " to the RIGHT");
    digitalWrite(Enable_PIN, LOW);
    digitalWrite(DIR_PIN, LOW);
  }

  stpDelay = map((11 - vel), 1, 10, minStepDelay, maxMovingStepDelay);

  int accelCycles = 50;
  int del = maxMovingStepDelay;
  int growRate = (maxMovingStepDelay - stpDelay) / accelCycles;

  long i;

  Serial.println("movement " + String(direct * steps * (mmPerRev * gearReduction) / stepsPerRev));
  
  //Serial.println("movement done = " + String(movementDone));
  for (i= 0; i < steps; i++) {
    //if (movementDone = 0){
      stepping(stpDelay, ignore);
      //Serial.println("movement done = " + String(movementDone));  
    //}else{
      //movementDone = 0;
    //  Serial.println("from moveby. Movement done was not zero");
      //movy();
    //  Serial.println("unfortunately you're here");
    //  break;
    //} 
  }
   
   curPosition = curPosition + mmDisplacement; //(curPosition + direct * i * (mmPerRev * gearReduction) / stepsPerRev);
   Serial.println("Steps done " + String(i) + " out of " + String(steps));
   Serial.println("The current position is " + String(curPosition) + " mm");
   Serial.println("-----------------------------------");
}


void moveUntil(float x, int vel) {
  // absolute stepping

  float mmToGo;
  
  //Serial.println("movement done is = " + String(movementDone));
  mmToGo = x - curPosition;
  Serial.println("move until " + String(x) +" , mm 2 go " + String(mmToGo) + " mm");
  moveBy(mmToGo, vel,0);
  
}


void stepping(int del, int ignore) {

  if (ignore = 0){
    if (digitalRead(lSwitch)){
      digitalWrite(STEP_PIN, HIGH);
      digitalWrite(inLed, HIGH);
      delayMicroseconds(del);                                        //  holding for some microseconds
      digitalWrite(STEP_PIN, LOW);
      digitalWrite(inLed, LOW);
      delayMicroseconds(del);
      //printOnLCD("Current Position is", String(curPosition));
      //Serial.println("stepping, ignore is zero and lswitch is true");
      //movy();
    }else if (!digitalRead(lSwitch)) {
      Serial.println("caution");
      //movementDone = 1;
      //Serial.println("stepping, ignore is zero and lswitch is false");
      //movy();
      //Serial.println("movement done is set to = " + String(movementDone));
      //displayNextStep("Limit Switch Hit", String(curPosition));
    }    
  }else if (ignore = 1){
      digitalWrite(STEP_PIN, HIGH);
      digitalWrite(inLed, HIGH);
      delayMicroseconds(del);                                        //  holding for some microseconds
      digitalWrite(STEP_PIN, LOW);
      digitalWrite(inLed, LOW);
      delayMicroseconds(del);
      //printOnLCD("Current Position is", String(curPosition));
      //Serial.println("stepping, ignore is 1");
      //movy();
      //movementDone = 0;   
  }


}

void homingPro() {

  while (digitalRead(lSwitch)) {        // while the limit switch is not pressed the routine goes
    moveBy(-10, homingVelocity,0);                   // moving backwards very slowly in order to find the limit switch
  }
  delay(500);


  while (!digitalRead(lSwitch)) {        // while the limit switch is not pressed the routine goes
    moveBy(10, homingVelocity,1);                   // moving backwards very slowly in order to find the limit switch
  }
  
  delay(500);
 
  moveBy(0.1, homingVelocity,0);          // additional steps for safety (can be avoided)
  curPosition = 0;
  delay(2000);

}

I’ll give you shortly an idea of the dimensions I used to make my sled, but please pay attention to the fact that those dimension are related to my saw and you might want to change them in order to fit it to your saw.

Stay Tuned

Max

Leave a comment