Ability to use the A rotay axis independently

Here is where you can request new features or special features.

Ability to use the A rotay axis independently

Postby johnsattuk » Fri Feb 25, 2022 11:46 am

Would be very usefull to be able to run the Rotary axis independently whilst also running a program.
This would enable some lathe type operations eg:- spindles, without the need for 4 axis software.

Need a checkbox on the setting page for "independent" :D
johnsattuk
 
Posts: 79
Joined: Thu Aug 12, 2021 1:24 pm

Re: Ability to use the A rotay axis independently

Postby cncdrive » Sat Feb 26, 2022 2:59 am

And how would that work in practise? Please explain.
cncdrive
Site Admin
 
Posts: 4741
Joined: Tue Aug 12, 2014 11:17 pm

Re: Ability to use the A rotay axis independently

Postby johnsattuk » Sat Feb 26, 2022 11:05 am

To avoid confusion "spindles = Stair spindles", may be called other things in other countries

So as an example I want to make a round "Candle stick" with a shapely body.
Assuming ( always risky :D ) the "A" axis is aligned along the "X" axis

Generate the Gcode to drive "X" along the axis and "Z" axis to generate the required shape , (perhaps digitize an existing shape if you wanted a copy)
I mount the stock in the chuck of the "A" axis, load an appropriate cutter in the M/C collet, probably a 2mm tapered ball point
I start the "A" azis at a constant low speed ,( perhaps 2 rpm), run the prog. which runs the "X" axis slowly along ( perhas 200 mm/min) while the "Z" axis (aligned on the centre line) cuts the profile.
Ideally would be able to control the rotary axis similar to the M3/M4 commands, so that speeds and feeds could be controlled from within the prog, two modes selectable (normal - constant)

I have used this method but with a seperately driven rotary axis, it works very well but a built in mode would be much more convenient and controllable
I have a Luthier friend who has used this method (also wih a seperately driven axis) to cut pegs for guitars, if you dont have a CNC lathe it allows to produce identical parts
johnsattuk
 
Posts: 79
Joined: Thu Aug 12, 2021 1:24 pm

Re: Ability to use the A rotay axis independently

Postby ger21 » Sat Feb 26, 2022 11:44 am

cncdrive wrote:And how would that work in practise? Please explain.


You basically make the A axis act like a spindle, so it runs continuously while the g-code is running. You just need a way to start, stop, and assign an RPM. You could just make some custom M codes.
With step/dir, this should be relatively simple, as it doesn't need to be considered in the trajectory planner. Unless that may not be simple. :D
Gerry
UCCNC 2022 Screenset - http://www.thecncwoodworker.com/2022.html
ger21
 
Posts: 2682
Joined: Sat Sep 03, 2016 2:17 am

Re: Ability to use the A rotay axis independently

Postby johnsattuk » Sat Feb 26, 2022 11:53 am

M33,M34,M35
johnsattuk
 
Posts: 79
Joined: Thu Aug 12, 2021 1:24 pm

Re: Ability to use the A rotay axis independently

Postby johnsattuk » Sat Feb 26, 2022 1:47 pm

AUX. Spindle :D
johnsattuk
 
Posts: 79
Joined: Thu Aug 12, 2021 1:24 pm

Re: Ability to use the A rotay axis independently

Postby johnsattuk » Wed Mar 02, 2022 10:02 am

The existing spindle setup includes an option for a step/dir spindle so much of the coding needed exists ?
Just needs an option to be able to use as a second spindle with it's own M commands :D
johnsattuk
 
Posts: 79
Joined: Thu Aug 12, 2021 1:24 pm

Re: Ability to use the A rotay axis independently

Postby A_Camera » Fri Mar 04, 2022 6:54 pm

johnsattuk wrote:To avoid confusion "spindles = Stair spindles", may be called other things in other countries

So as an example I want to make a round "Candle stick" with a shapely body.
Assuming ( always risky :D ) the "A" axis is aligned along the "X" axis

Generate the Gcode to drive "X" along the axis and "Z" axis to generate the required shape , (perhaps digitize an existing shape if you wanted a copy)
I mount the stock in the chuck of the "A" axis, load an appropriate cutter in the M/C collet, probably a 2mm tapered ball point
I start the "A" azis at a constant low speed ,( perhaps 2 rpm), run the prog. which runs the "X" axis slowly along ( perhas 200 mm/min) while the "Z" axis (aligned on the centre line) cuts the profile.
Ideally would be able to control the rotary axis similar to the M3/M4 commands, so that speeds and feeds could be controlled from within the prog, two modes selectable (normal - constant)

I have used this method but with a seperately driven rotary axis, it works very well but a built in mode would be much more convenient and controllable
I have a Luthier friend who has used this method (also wih a seperately driven axis) to cut pegs for guitars, if you dont have a CNC lathe it allows to produce identical parts

This is not a big problem, if you can control your spindle speed separately. I have just tested, and it works for me, except that I control my high speed spindle via Modbus, and can not set the speed manually because UCCNC limits the maximum RPM. I know, it sounds confusing, so I try to explain...

I have a DIY lathe and a CNC. The lathe spindle is a servo motor, which I can run as a rotary axis as well, and is configured as a step / dir spindle when I want to use it as a lathe. The thing is that in the same tab, where you configure the spindle, you must set a minimum and a maximum rpm, which in my case is set to 1 (minimum) and 1000 (maximum). These values are my lathe chuck RPM values, but because of these values, UCCNC sends maximum 1000 even to my high speed spindle, so I can't spin that one at 24000 RPM and the lathe chuck at 1000 RPM at the same time, unless I override one or the other. However, if you are familiar with Arduino programming then you could build my control box which I designed for similar use, and that can rotate your rotary axis at any speed (code of the last version included below). Or you set up and run the rotary stepper as step / dir spindle, while controlling your high speed spindle manually. If UCCNC had independent min / max RPM settings it could have been done using UCCNC as well, but for now, it can only control one spindle fully automatic.

Code: Select all
//------------------------------------------------------------------------------------------------------------
//
//  Version information to display at start:
//
#define Ver_nr "3.03"     
#define Ver_date "2021-12-20"
//
//  This software is created by Adapting Camera. You are free to use it on a non-commercial basis.
//
//  YouTube: https://www.youtube.com/c/AdaptingCamera/videos
//  Blogger: https://adapting-camera.blogspot.com/
//
//  This is a program to run a stepper motor an infinite number of steps in CW or CCW direction
//  to allow constant rotation. The idea is to use a 4th axis of a CNC as a lathe.
//  This will result in a sort of "poor man's lathe", with limited useability, but enough for my needs for now.
// 
//  Functionality is simple, three buttons, one for each direction and one for stoping the stepper rotation.
//  Maximum speed is 1000 rpm with a servo motor implementation, which in is equal to 3000 RPM for the servo
//  motor, due to the 1:3 reduction gear. The chuck RPM can be set via a potentiometer
//  between 0 - Chuck_rpm_max, which is set to 1000 below. Speed selection can be set to "Low" or "High" by
//  flipping the Low/High switch. Flipping to "Low" means that the read potentiometer value will be divided
//  by the value defined by SlowSpeed. This makes setting of slow speed easier.
//
//  This servo is controlled as a stepper (with DIR, STEP and ENABLE signals), but it is a real AC servo.
//
//  Stepper motor acceleration and deceleration is implemented, which can be set in seven steps.
//  Fastest is Max_Acceleration (10000) steps/s² and that value is divided by the value of the BCD wheel value.
//  The push button of the rotary encoder is connected to the Arduino reset and acts as an emergency stop.
//
//  Uses interrupt for the stepper pulse generation.
//
//  The three buttons and the acceleration BCD decoder are outside the interrupt chain and are read in the
//  normal program loop. This has the disadvantage of a bit slower reaction, so a very short push may not
//  result in any reaction. I may change this later to handle it as state change IRQ on the inputs,
//  but I think this is good enough for now.
//
//  This code is tested only with Arduino Uno, but it will probably work also with other Arduino devices.
//
//  Please note that the FastAccelStepper has a built in limitation regarding Arduino Uno. The number of pulses
//  per second is by default limited to 25000. To run faster, if you need more pulses per second then some
//  modifications are necessary in the FastAccelStepper.h file.
//
//  Change: MIN_DELTA_TICKS (TICKS_PER_S / 25000) to: MIN_DELTA_TICKS (TICKS_PER_S / 100000) which is more than
//  necessary, but I don't see any point for this limit of 25000 pulses/s so I just enter this high number.
//  This allows setting of the SpeedInHz to 80kHz with some margins for fluctuations and that will let me set
//  maximum of 1000rpm on the chuck using 3:1 gear and 1/16 microstepping.
//
//  Good luck.
// 
#include "FastAccelStepper.h"
#include <LiquidCrystal.h>

#define Step_Pin    10
#define Dir_Pin     11
#define Enable_Pin  12

#define RightButton 2
#define LeftButton  3
#define DownButton  A1 

#define BCD_0       A3 
#define BCD_2       A4 
#define BCD_4       A5 
volatile byte Read_BCD = 0;
volatile byte  Old_BCD = 0;

int Gear_Ratio = 3.0;                         // 60:20 gear in rotary axis
//float Chuck_rpm_k = (1/0.15) * Gear_Ratio;  // 1:4 microstep. This is a constant which is used for chuck RPM for frequency conversion
float Chuck_rpm_k = (1/0.075) * Gear_Ratio; // 1:8 microstep. This is a constant which is used for chuck RPM for frequency conversion
//float Chuck_rpm_k = (1/0.0375) * Gear_Ratio;  // 1:16 microstep. This is a constant which is used for chuck RPM for frequency conversion
 
int Chuck_rpm_max = 1000; // Maximum rpm for the chuck (for servo motor)
int Running_rpm;          // This is the speed now
int Value = 0;
#define NUMSAMPLES  10    // Number of samples to take and average to get a more stable reading
#define SlowSwitch  A2    // Slow speed selection switch
#define SlowSpeed   10    // This value is the divisor, which is used if slow speed is selected
#define SpeedInput  A0    // Speed selector potentiometer input

uint32_t SpeedInHz;
uint32_t SpeedInUs;
int32_t Acceleration;           // Set after the read of BCD wheel
#define Max_Acceleration 13000  // Set as default acceleration 10000 steps/s²

volatile boolean  START_STOP = false;
volatile boolean  Running_CW = true;
volatile boolean  Enable_ON  = false;
volatile boolean  Running    = false;

volatile byte A_Flag          = 0;  // Rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte B_Flag          = 0;  // Rising edge on pinB to signal that the encoder has arrived at a detent
volatile int  Encoder_Pos     = 0;  // Current value of encoder position.
volatile byte EEPROM_Enc_pos  = 0;  // EEPROM adress of the saved encoder position. This adress read at start.
volatile int  Old_Enc_Pos     = 0;  // Last encoder position value.
volatile byte Reading         = 0;  // Direct values read from our interrupt pins before checking to see if moved a whole detent
volatile int  ms              = 0;  // Time between roraty encoder pulses

FastAccelStepperEngine Engine = FastAccelStepperEngine();
FastAccelStepper *A_stepper = NULL;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);    // Use these IO pins for LCD RS,E,D4,D5,D6,D7

void setup() {
//  Serial.begin(115200);
  lcd.begin(16, 2);                 // Initialize the 20 x 2 LCD
 
  Engine.init();
  A_stepper = Engine.stepperConnectToPin(Step_Pin);
  A_stepper->setDirectionPin(Dir_Pin);
  A_stepper->setEnablePin(Enable_Pin);
  A_stepper->setAutoEnable(true);

  A_stepper->setSpeedInHz(SpeedInHz);
  A_stepper->setAcceleration(Acceleration);
  pinMode (RightButton, INPUT_PULLUP);
  pinMode (DownButton,  INPUT_PULLUP);
  pinMode (LeftButton,  INPUT_PULLUP);

  pinMode (BCD_0,       INPUT_PULLUP);
  pinMode (BCD_2,       INPUT_PULLUP);
  pinMode (BCD_4,       INPUT_PULLUP);

  digitalWrite(RightButton, HIGH);  //use the internal pullup resistor
  digitalWrite(DownButton,  HIGH);  //use the internal pullup resistor
  digitalWrite(LeftButton,  HIGH);  //use the internal pullup resistor

  digitalWrite(BCD_0,       HIGH);  //use the internal pullup resistor
  digitalWrite(BCD_2,       HIGH);  //use the internal pullup resistor
  digitalWrite(BCD_4,       HIGH);  //use the internal pullup resistor

  pinMode (SlowSwitch,  INPUT_PULLUP);
   
  lcd.setCursor(0,0);                     // Erase the display
  lcd.print("                ");
  lcd.setCursor(0,1);
  lcd.print("                ");
  lcd.setCursor(0,0);                     // Display the values
  lcd.print("Version: ");
  lcd.print(Ver_nr);
  lcd.setCursor(0,1);
  lcd.print("Date: ");
  lcd.print(Ver_date);

  delay (2000);
  DisplayStaticOnLCD();
  DisplayOnLCD();
}

//------------------------------------------------------------------------------------------------------------
//
//  Start the main loop
//
//  Read the BCD wheel, the rotary encoder and three buttons in the loop.
//
void loop() {

//------------------------------------------------------------------------------------------------------------
//
//  Read the BCD encoder and set acceleration accordingly.
//  The acceleration is set by dividing Max_Acceleration with the value of the encoder.
//
    Read_BCD = PINC & B00111000;                  // Read port C and mask the BNC encoder bits
    Read_BCD = Read_BCD >> 3;                     // Right shift 3 times
    if(Old_BCD != Read_BCD) {
      Acceleration = int(Max_Acceleration / Read_BCD);   // Calculate the divisor
      A_stepper->setAcceleration(Acceleration);     // Set the new acceleration value     
      Old_BCD = Read_BCD;
    }
////------------------------------------------------------------------------------------------------------------
////
////  Read the potentiometer input and set chuck rpm accordingly.
////  The rpm is set through converting the encoder value to pulse time in microseconds.
////  This value is used as speed value for the stepper.
////
  Value = 0;
  for(int i = 0; i < NUMSAMPLES; i ++){
    Value = Value + analogRead(SpeedInput);    // read the input NUMSAMPLES times to avarage value
  }
  Value = Value / NUMSAMPLES;                  // Average out the readings

  delay(100);
  Encoder_Pos = int ( Value * (Chuck_rpm_max / 1023.0));
  if (digitalRead(SlowSwitch) == 0) {
    Encoder_Pos = Encoder_Pos / 10.0;
  }
  if ( Encoder_Pos <= 0 ) {
    Encoder_Pos = 0;
    SpeedInHz = 0;
  } else {
    if ( Encoder_Pos >= Chuck_rpm_max ) {
      Encoder_Pos = Chuck_rpm_max;
    }
  }
  if ( Encoder_Pos << 0 ) {
    SpeedInHz = (Encoder_Pos * Chuck_rpm_k);
  }
  lcd.setCursor(8,0);                     // Display the values
  lcd.print("    ");
  lcd.setCursor(8,0);
  lcd.print(Encoder_Pos);
  lcd.setCursor(8,1);

//------------------------------------------------------------------------------------------------------------
//
//  Read buttons to set rotation direction or to stop the rotation
//
  if ( digitalRead(DownButton) == false) {    // Stop with decelerating to zero RPM
    A_stepper->stopMove();
    Running_rpm = 0;
    Running = false;
    DisplayOnLCD();
//    Serial.println("Stop");
  }

  if (digitalRead(LeftButton) == false) {   // Set CCW rotation...
    if (Running_CW == true) {               // ...but only if not rotating to CCW now.
      A_stepper->stopMove();
      if (Running == true) {
        Running = false;
        delay (1500);
      }
      Running_CW = false;
    }
    if ( Encoder_Pos >> 0 ) {
      A_stepper->setAcceleration(Acceleration);
      A_stepper->setSpeedInHz(SpeedInHz);
      A_stepper->runBackward();
      Running = true;
    } else {
      A_stepper->stopMove();
      Running = false;
    }
    Running_rpm = Encoder_Pos;
    DisplayOnLCD();
//    Serial.print("Left: ");
//    Serial.println(SpeedInHz);
  }

  if ( digitalRead(RightButton) == false) {   // Set CW rotation...
    if (Running_CW == false) {                // ...but only if not rotating to CW now.
      A_stepper->stopMove();
      if (Running == true) {
        Running = false;
        delay (1500);
      }
      Running_CW = true;
    }
    if ( Encoder_Pos >> 0 ) {
      A_stepper->setAcceleration(Acceleration);
      A_stepper->setSpeedInHz(SpeedInHz);
      A_stepper->runForward();
      Running = true;
    } else {
      A_stepper->stopMove();
      Running = false;
    }
    Running_rpm = Encoder_Pos;
    DisplayOnLCD();
//    Serial.print("Right: ");
//    Serial.println(SpeedInHz);
  }
} // End loop

void DisplayOnLCD() {
  lcd.print("    ");
  lcd.setCursor(8,1);
  lcd.print(Running_rpm);
 
  if (Running_rpm == 0) {
    lcd.setCursor(12,1);
    lcd.print("STOP");   
  } else {
    if (Running_CW == false) {
      lcd.setCursor(12,1);
      lcd.print(" CCW");
    } else {
        lcd.setCursor(12,1);
        lcd.print(" CW ");
    }
  }
}
void DisplayStaticOnLCD() {
  lcd.setCursor(0,0);                     // Erase the display
  lcd.print("                    ");
  lcd.setCursor(0,1);
  lcd.print("                    ");
  lcd.setCursor(0,0);                     // Display the values
  lcd.print("Set RPM:");
  lcd.setCursor(0,1);
  lcd.print("Run RPM:");
}
A_Camera
 
Posts: 638
Joined: Tue Sep 20, 2016 11:37 am

Re: Ability to use the A rotay axis independently

Postby johnsattuk » Fri Mar 04, 2022 8:16 pm

I had decided to drive the rotary axis with the spindle Step/Dir setup and to control the main spindle speed manualy and configure a screen button to stop start it, could tie it into the ES and cycle stops that way.
I do quite like controlling the spindle speed manually, it enables to find the sweetspot of the relative flimsy nature of most DIY routers, however it would have been a nice feature if incorperated in the software. :)
johnsattuk
 
Posts: 79
Joined: Thu Aug 12, 2021 1:24 pm


Return to Feature Request

Who is online

Users browsing this forum: No registered users and 2 guests