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"
![Very Happy :D](./images/smilies/icon_e_biggrin.gif)
cncdrive wrote:And how would that work in practise? Please explain.
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) 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
//------------------------------------------------------------------------------------------------------------
//
// 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:");
}
Users browsing this forum: No registered users and 10 guests