// by McMax
// ELS - Electronic Lead Screw
// Firmware V1.6 - 27/11/2019
// 24/06/2021 Sistemata variabile movimento libero
// Encoder PULL_UP defined by default
// JoyStick Shield Funduino
#include <LiquidCrystal_I2C.h> //include la libreria di controllo del display LCD
#include <EEPROM.h> //include la libreria per il controllo della EEPROM
#include <avr/pgmspace.h> //include la libreria per l'utilizzo della flash come storage (per le stringhe)
#define firmware_version "v1.6 - Rev5 by EBG"
//08.07.2021 Qualche aggiustamento al menu' avanzamenti con inserimento dei controlli tramite SEL2
//01.07.2021 Qualche aggiustamento al menu' avanzamenti
//19.06.2021 Ho reso disattivabile la scatola norton dalle impostazioni i valori di avanzamento restano quelli di definiti per NORTON_gearbox=0
//17.06.2021 Menu impostazioni gestito in fase di avvio premendo il tasto ESC o quando i valori eprom non sono validi variabile SHOW_Impostazioni
//
// Pin assignement
#define ANALOG_X A0 //ingresso analogico joystick asse X
#define ANALOG_Y A1 //ingresso analogico joystick asse Y
#define STEP 9 //uscita segnale "STEP" per motore stepper
#define DIR 10 //uscita segnale "DIR" per motore stepper
#define ENCA 2 //ingresso ancoder A - INTERRUPT 0
#define ENCB 3 //ingresso ancoder B - INTERRUPT 0
#define ENABLE 7 //uscita segnale "ENABLE" per motore stepper
#define ESC 6 //bottone ESC - Pulsante A modificato per utilizzare pin pulsante E
#define SEL 4 //bottone SELECT - Pulsante C
#define SEL2 8 //bottone SELECT - Pulsante sotto joy K
#define RESET 5 //bottone RESET - Pulsante D
#define BEEP 12 //uscita buzzer 12
// END pin assignment
#define MaxSteps 800 //dimension of the step array - this can be adjusted to increase the maximum pitch according to the available RAM
/*
//Analog Joystick reading tolerance definition
#define LOW_TOL 250 //minimum tolerance for joystick movement LOW (2.2V on joystic pot cursor)
#define HIGH_TOL 573 //minimum tolerance for joystick movement HIGH (2.8V on joystic pot cursor)
#define LOW_1 298 //first LOW pos tolerance for joystick movement (1.7V on joystic pot cursor)
#define LOW_2 196 //second LOW pos tolerance for joystick movement (1.2V on joystic pot cursor)
#define LOW_3 93 //third LOW pos tolerance for joystick movement (0.7V on joystic pot cursor)
#define HIGH_1 625 //first HIGH pos tolerance for joystick movement (3.3V on joystic pot cursor)
#define HIGH_2 727 //second HIGH pos tolerance for joystick movement (3.8V on joystic pot cursor)
#define HIGH_3 830 //third HIGH pos tolerance for joystick movement (4.3V on joystic pot cursor)
//END Analog Joystick reading tolerance definition
*/
//Analog Joystick reading tolerance definition
#define LOW_TOL 410 //minimum tolerance for joystick movement LOW (2.2V on joystic pot cursor)
#define HIGH_TOL 610 //minimum tolerance for joystick movement HIGH (2.8V on joystic pot cursor)
#define LOW_1 290 //first LOW pos tolerance for joystick movement (1.7V on joystic pot cursor)
#define LOW_2 190 //second LOW pos tolerance for joystick movement (1.2V on joystic pot cursor)
#define LOW_3 90 //third LOW pos tolerance for joystick movement (0.7V on joystic pot cursor)
#define HIGH_1 710 //first HIGH pos tolerance for joystick movement (3.3V on joystic pot cursor)
#define HIGH_2 910 //second HIGH pos tolerance for joystick movement (3.8V on joystic pot cursor)
#define HIGH_3 1010 //third HIGH pos tolerance for joystick movement (4.3V on joystic pot cursor)
//END Analog Joystick reading tolerance definition
// initialize the LCD library with the references of the interface pins
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 20 chars and 4 line display
//LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7); // Set the LCD I2C address
#define PRESSED LOW //costante per determinare la pressione dei pulsanti
char buff[21]; //buffer per memorizzare le stringhe lette dalla flash e visualizzate sul display
boolean mm_min = false; //variabile che determina la modalita' di avanzamento: true = mm/min; false = cent/giro
unsigned int av_carro[4]; //array da 4 valori che memorizza di avanzamenti dei rapporti norton per il carro caricandoli dalla EEPROM
unsigned int av_trasv[4]; //array da 4 valori che memorizza di avanzamenti dei rapporti norton per il trasversale caricandoli dalla EEPROM
byte NORTON_gearbox; //valore che memorizza la posizione corrente della scatola norton (usato come indice per gli array qui sopra)
boolean NORTON = true; //valore che indica se la scatola norton e' OFF
// variabili relative ad encoder e motore stepper
int one_turn_mandrel_steps; //numero di step/giro dell'encoder mandrino (x4) (valore caricato da EEPROM)
float screw_pitch; //passo della vite madre in mm (valore caricato da EEPROM)
unsigned int one_turn_screw_steps; //numero di step/giro del motore stepper sulla vite (valore caricato da EEPROM)
float single_step_pitch; //valore in micron di movimento del carro ad ogni step della vite madre
//calcolato al termine della funzione LoadFromEEPROM()
float SingleStepFeed; //valore in millimetri di movimento del carro ad ogni step della barra
//calcolato nelle funzioni di avanzamento dove richiesto ed in base alla posizione del cambio norton
float distance = 0.00; //distanza in mm usata nella funzione FilettaturaToPosition()
// fine variabili relative ad encoder e motore stepper
// mixed variables
long OldSpeedTimer;
long OldPos = 0;
long NewPos = 0;
//end mixed variables
//variabili usate per il calcolo della progressione di filettatura
byte sequenza [MaxSteps]; //array usato per i calcolo della sequenza passi encoder/stepper. la lunghezza di questo array limita il massimo passo eseguibile in filettatura
//max_passo = passo_vite*(800/passi_stepper_giro)
int numero_passi = 0; //variabile che memorizza il numero dei passi stepper per ogni giro mandrino (usata in filettatura e avanzamento)
int pointer = 0; //puntatore per array "sequenza"
boolean Metric = true; //se vero il passo impostato in filettatura e' metrico, se Falso e' imperial
float thread_pitch = 1.00; //passo impostato per la filettatura in mm (standard 1.00mm)
byte TPI = 20; //passo impostato per la filettatura in pollici (standard 20 TPI)
//fine variabili usate per il calcolo della progressione di filettatura
//variabili relative alla lettura dell'encoder
int steps = 0; //passi encoder relativi - variabile usata come appoggio in filettatura e avanzamento
volatile long absolute_encoder_steps = 0; //passi encoder assoluti
boolean step_flag; //Flag per detarminare se il passo encode avvenuto (usata nella rountine di interrupt di lettura dell'nencoder)
int passi_sequenza = 0;
char encoder[] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0}; //arrray da 16 valori usato per "muovere" i passi enoder nella rountine interrupt di filettatura
boolean sviluppo_filetto = true; //Flag che detrmina il verso id filettatura: TRUE = DESTRO; FALSE = SINISTRO
//fine variabili relative alla lettura dell'encoder
// Stepper motor variables
boolean CW; //TRUE se la rotazione standard della vite e' in senso orario (orario trascina il carro verso il madnrino). variabile letta da EEPROM
boolean CCW; //TRUE se la rotazione standard della vite e' in senso anti-orario (anti-orario trascina il carro verso il madnrino) variabile letta da EEPROM
boolean Direction = CW; //variabile usata per determinare il senso di rotazione momentaneo del motore stepper e incrementare o decrementare i passi
boolean CarroTrasv = true; //TRUE = avanzamneto sul carro; FALSE = avanzamento sul trasversale
int passi_stepper = 0;
unsigned long Speed; //memorizza la velocit corrente del motore stepper in giri al minuto - se = 0 lo stepper e' fermo
unsigned long MaxStepperSpeed; //massima velocit di rotazione ammessa per il motore stepper. Variabile letta da EEPROM
volatile unsigned int TOP = 65535; //valore usato per scrivere il registro ICR1 che determina la frequenza del PWM che regola la velocit di rotazione del motore stepper
volatile long absolute_steps = 0; //passi stepper assoluti
unsigned int AccelerationDelay; //ritardo accelerazione del motore stepper - varibile letta da EEPROM
unsigned int DecelerationDelay; //ritardo decelerazione del motore stepper - varibile letta da EEPROM
int thread_offset_steps; //offset per operazione di filettatura. Indica quanti passi prima dell'inizio del filetto si deve posizionare la vite
int feed = 1; //valore corrente avanzamneto usato nella funzione di avanzamneto
int beep = 1; //beep
// End Stepper motor variables
boolean SHOW_Impostazioni = false; //variabile che determina la visualizzazione delle Impostazioni nel menu.
//******************************************** E N D D E C L A R A T I O N **************************************//
void StopTimer1() //pulisce i registri del Timer1 per fermare il PWM e quindi il motore stepper
{
TCCR1B &= B11111000; //imposta il prescaler del Timer a 0 - il Timer1 si ferma in questo punto
TIMSK1 &= B11111101; //ferma l'interrupt associato al Timer1
TCNT1 = 0; //resetta il valore del Timer1
}
void StartTimer1() //imposta i registri del Timer 1 e fa partire il conteggio - riavvia il motore dopo che stato fermato
{
TCNT1 = 0; //resetta il valore del Timer1
TIMSK1 |= B0000010; //imposta l'interrupt associato al Timer1
TCCR1B |= B0000011; //imposta il precaler del Timer1 a clk/64 - il Timer1 parte in questo punto
}
ISR (TIMER1_COMPA_vect) // rountine di interrupt del Timer1 - usata per incrementare i passi stepper e tenere traccia della posizone
{
ICR1 = TOP;
if (Direction == CW)
absolute_steps++;
else
absolute_steps--;
}
void beepON()
{
if (beep == 1) //Beep on completion of move if beep switched on
{
tone(BEEP, 4000, 100); //tone(pin,frequency,duration-milliseconds)
}
}
void(* resetFunc) (void) = 0;
void StepperON() //accende il motore stepper
{
digitalWrite(ENABLE, HIGH);
delay(10);
digitalWrite(ENABLE, LOW);
}
void StepperOFF() //spegne il motore stepper
{
digitalWrite(ENABLE, LOW);
}
void ClearPWM() //Clear the PWM and stops Timer1
{
TCCR1B = 0; // halt Timer by setting clock bits (Prescaler) to 0. This will stop the timer until we get set up
TCCR1A = 0; // Reset the register to avoid misconfiguration issue
TCNT1 = 0; // start counting at 0 (BOTTOM)
TIMSK1 = 0; // Reset the interrupt mask register for Timer1
}
void SetPWM() //Sets the PWM and starts Timer1
{
ICR1 = 65535; //(65535)set TOP at maximum value in order to get minumun frequency on the motor with prescaler clk/64
OCR1A = 3; // Set OCR1A register to 1 which means that the CLEAR of the OC1A pin is done after 2 timer1 cycle.
// means 8us with clk/64 prescaler
TCCR1A = _BV(COM1A1) | _BV(WGM11); //OC1A set for PWM with SET at BOTTOM and CLEAR at MATCH (non-inverted mode)
TCCR1B = _BV(WGM13) | _BV(WGM12); //WGM set for Fast PWM with ICR1 used as TOP value
}
void SetOneShot()
{
ICR1 = 0; // Set ICR1 register to 0. ICR1 is the TOP of the timer counting which means the timer is stuck to 0
OCR1A = 65534; // Set OCR1A to 65534 in order to fire a pulse of 2 cycle clock (8us)
TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11); //OC1A set for PWM with CLEAR at BOTTOM and SET at MATCH (inverted mode)
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10); //WGM set for Fast PWM with ICR1 used as TOP value. Prescaler set to clk/64 (4uSec cycle)
// Timer1 start HERE
}
void FireStep()
{
TCNT1 = 65533; // Force the Time1 value to 65533 in order to hit the MATCH of OCR1A after one clock cycle
}
void StepperMoveToPosition(long absolute_position) //runs the stepper to a specific position without accelerating (this is used for precision positioning only
//please make sure to call SetOneShot() before using this function
{
if (absolute_steps < absolute_position) digitalWrite(DIR, CCW); else digitalWrite(DIR, CW);
while (absolute_steps != absolute_position)
{
FireStep();
if (digitalRead(DIR) == CW) absolute_steps--; else absolute_steps++;
delayMicroseconds(1500);
}
}
void StepperRunToSpeed(unsigned long Target) //Runs the stepper to a specific Target speed (in rpm)
//AccelRate is the wait time in usec for acceleration
{
unsigned long CurrentSpeed;
if (Target > MaxStepperSpeed) Target = MaxStepperSpeed;
if (Target == 0)
{
for (CurrentSpeed = Speed; CurrentSpeed > 0; CurrentSpeed--)
{
TOP = ((1.0/((CurrentSpeed*one_turn_screw_steps)/60))*1000000)/4;
delayMicroseconds(DecelerationDelay);
}
StopTimer1();
}
else //IF target != 0
{
if (Speed == 0) StartTimer1();
if (Target > Speed)
{
if (Speed == 0) Speed = 1;
for (CurrentSpeed = Speed; CurrentSpeed <= Target; CurrentSpeed++)
{
TOP = ((1.0/((CurrentSpeed*one_turn_screw_steps)/60))*1000000)/4;
delayMicroseconds(AccelerationDelay);
}
}
else //Taget is < than Speed
{
for (CurrentSpeed = Speed; CurrentSpeed >= Target; CurrentSpeed--)
{
TOP = ((1.0/((CurrentSpeed*one_turn_screw_steps)/60))*1000000)/4;
delayMicroseconds(DecelerationDelay);
}
}
}
Speed = Target;
} //END of the Interrupt Service Routine
void setup()
{
// lcd.init();
lcd.begin (20,4); // for 20 x 4 LCD module
// lcd.setBacklightPin(3,POSITIVE);
// lcd.setBacklight(HIGH);
lcd.backlight();
lcd.clear();
lcd.home ();
lcd.print(" Cambio Elettronico");
lcd.setCursor(3,1);
lcd.print("Scatola Norton");
lcd.setCursor(5,2);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print(firmware_version);
beepON();
delay(3000);
pinMode(ENCA, INPUT_PULLUP); //Econder A input - INTERRUPT
pinMode(ENCB, INPUT_PULLUP); //Encoder B input - INTERRUPT
pinMode(ESC, INPUT_PULLUP); //ESC BUTTON (D4 of the sparkfun Joystick shield)
pinMode(SEL, INPUT_PULLUP); //SELECTION BUTTON (D5 of the sparkfun Joystick shield)
pinMode(SEL2, INPUT_PULLUP); //SELECTION BUTTON (K of the sparkfun Joystick shield)
pinMode(RESET, INPUT_PULLUP); //RESET BUTTON (D6 of the sparkfun Joystick shield)
pinMode(STEP, OUTPUT); //STEP OUTPUT to stepper driver
pinMode(DIR, OUTPUT); //DIRection OUTPUT to stepper driver
pinMode(ENABLE, OUTPUT); //ENABLE OUTPUT to stepper driver
pinMode(BEEP, OUTPUT); //ENABLE OUTPUT to stepper driver
LoadFromEEPROM(); //Load the settings stored in the EEPROM and place it in the variables
absolute_steps = 0; //initialize the abolute number of leadscrew stepper steps
absolute_encoder_steps = 0; //initialize the abolute number of mandrel encoder steps
Speed = 0; //initialize the current stepper speed to 0
}
void loop()
{
if (digitalRead(RESET) == PRESSED || !(one_turn_mandrel_steps >= 1000) ) { SHOW_Impostazioni = true; delay(5000);} //Verifico se è premuto il tasto RESET o se ci sono valori non validi nella EPROM
char scelta;
if ( SHOW_Impostazioni ) {
lcd.clear();
Impostazioni();
}
else
{
while (true)
{
scelta = Principale();
switch (scelta) {
case 1: lcd.clear(); Avanzamento(); break;
case 2: lcd.clear(); Filettatura(); break;
case 3: lcd.clear(); MovimentoLibero(); break;
case 4: lcd.clear(); MandrelSpeed(); break;
case 5: lcd.clear(); PosizioneAngolare(); break;
//case 6: lcd.clear(); Impostazioni(); break;
}
}
}
}
const char AVA0[] PROGMEM = " ** AVANZAMENTO ** ";
const char AVA1[] PROGMEM = "Long./Trasv.";
const char AVA2[] PROGMEM = "Avanzam. libero";
const char AVA3[] PROGMEM = "Avanzam. vincolato";
const char AVA4[] PROGMEM = "Imposta 'NORTON'";
const char AVA5[] PROGMEM = "Modo avanzamento";
const char AVA6[] PROGMEM = "***"; //Empty space for additional functions
const char AVA7[] PROGMEM = "longitudinale";
const char AVA8[] PROGMEM = "trasversale ";
const char AVA9[] PROGMEM = "SEL: OK";
const char AVA10[] PROGMEM = "ESC:fine";
const char AVA11[] PROGMEM = "SEL:start ";
const char AVA12[] PROGMEM = "SEL:stop ";
const char AVA13[] PROGMEM = " <=== ";
const char AVA14[] PROGMEM = " ===> ";
const char AVA15[] PROGMEM = "Posizione";
const char AVA16[] PROGMEM = "Pos";
const char AVA17[] PROGMEM = "mm";
const char AVA18[] PROGMEM = "==";
const char AVA19[] PROGMEM = "mm/min";
const char AVA20[] PROGMEM = "RPM";
const char AVA21[] PROGMEM = "SEL:cambia";
const char AVA22[] PROGMEM = "cent/g";
const char* const MenuAvanzamento[] PROGMEM = {AVA0, AVA1, AVA2, AVA3, AVA4, AVA5, AVA6,
AVA7, AVA8, AVA9, AVA10, AVA11, AVA12, AVA13,
AVA14, AVA15, AVA16, AVA17, AVA18, AVA19, AVA20,
AVA21, AVA22};
void ImpostaModalitaAvanzamento()
{
boolean stay = true;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[0])));
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[5])));
lcd.setCursor(2, 1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[9])));
lcd.setCursor(0, 3);
lcd.print(buff);
while (stay)
{
if ((analogRead(ANALOG_Y) < LOW_1) || (analogRead(ANALOG_Y) > HIGH_1)) {mm_min = !(mm_min); beepON(); delay (400);}
if (mm_min) strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[19])));
else strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[22])));
lcd.setCursor(1, 2);
lcd.print(buff);
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED || digitalRead(ESC) == PRESSED) {stay = false; beepON(); delay (500);}
}
delay(300);
}
char AvanzamentoMenu()
{
static char Current;
char previous = 4;
char next = 2;
boolean stay = true;
boolean innerstay = true;
lcd.clear();
lcd.setCursor(0, 2);
lcd.print(">");
lcd.setCursor(19, 2);
lcd.print("<");
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[0])));
lcd.setCursor(0, 0);
lcd.print(buff);
while (stay)
{
switch (Current) {
case 0: Current = 6; break;
case 7: Current = 1; break;
}
previous = Current - 1;
next = Current + 1;
if (previous == 0)
previous = 6;
if (next == 7)
next = 1;
if ( !NORTON ) // Se la scatola norton è disattivata salto i menu
{
switch (previous) {
//disabilito menu trasversale 1 e menu norton 4 e il menu 6 empty space
case 1: previous = 5; Current = 2; next = 3; break;
case 2: previous = 2; Current = 3; next = 5; break;
case 3: previous = 3; Current = 5; next = 2; break;
case 4: previous = 5; Current = 2; next = 3; break;
case 5: previous = 5; Current = 2; next = 3; break;
case 6: previous = 2; Current = 3; next = 5; break;
}
}
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[previous])));
lcd.setCursor(1, 1);
lcd.print(" ");
lcd.setCursor(1, 1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[Current])));
lcd.setCursor(1, 2);
lcd.print(" ");
lcd.setCursor(1, 2);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[next])));
lcd.setCursor(1, 3);
lcd.print(" ");
lcd.setCursor(1, 3);
lcd.print(buff);
innerstay = true;
while (innerstay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) {
stay = false;
innerstay = false;
}
if (analogRead(ANALOG_Y) < LOW_1) {
Current -= 1;
innerstay = false;
beepON();
}
if (analogRead(ANALOG_Y) > HIGH_1) {
Current += 1;
innerstay = false;
beepON();
}
if (digitalRead(ESC) == PRESSED) {
Current = 7;
stay = false;
innerstay = false;
}
}
delay (300);
}
return Current;
}
void ImpostaNORTON()
{
boolean stay = true;
boolean innerStay = true;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[0])));
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[4])));
lcd.setCursor(1, 1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[9])));
lcd.setCursor(0, 3);
lcd.print(buff);
while (stay)
{
lcd.setCursor(1, 2);
lcd.print(NORTON_gearbox + 1);
innerStay = true;
while (innerStay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) {
innerStay = false;
stay = false;
}
if (analogRead(ANALOG_Y) < LOW_1) {
innerStay = false;
if (NORTON_gearbox > 0) NORTON_gearbox --;
delay(400);
}
if (analogRead(ANALOG_Y) > HIGH_1) {
innerStay = false;
if (NORTON_gearbox < 3) NORTON_gearbox ++;
delay(400);
}
}
}
EEPROM.put(32, NORTON_gearbox); // Saves the new NORTON GEARBOX POSITION in the EEPROM
delay (300);
}
void LongTrasv()
{
boolean stay = true;
boolean innerStay = true;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[0])));
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[1])));
lcd.setCursor(1, 1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[9])));
lcd.setCursor(0, 3);
lcd.print(buff);
while (stay)
{
if (CarroTrasv) strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[7]))); else strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[8])));
lcd.setCursor(1, 2);
lcd.print(buff);
innerStay = true;
while (innerStay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) {
innerStay = false;
stay = false;
delay(400);
}
if ((analogRead(ANALOG_Y) < LOW_1) | (analogRead(ANALOG_Y) > HIGH_1)) {
innerStay = false;
CarroTrasv = !(CarroTrasv);
delay(400);
}
}
}
delay (300);
}
void LCD_print_float(float value, byte col, byte lin) //print a floating point value between -999.99 and 999.99 with always 3 integer digits (000 mask)
{
float assoluto = abs(value);
lcd.setCursor(col, lin);
if (value >= 0) lcd.print(" "); // value is 0 or positive, print a space
else lcd.print("-"); // value is negative, print the minus before the zeros
if (assoluto < 10) {
lcd.print("00"); //value has 1 digit + 2 decimals
lcd.print(assoluto, 2);
}
if ((assoluto >= 10) && (assoluto < 100)) {
lcd.print("0"); //value has 2 digits + 2 decimals
lcd.print(assoluto, 2);
}
if ((assoluto >= 100) && (assoluto < 1000)) lcd.print(assoluto, 2); //value has 3 digits + 2 decimals
//lcd.print(" ");
lcd.setCursor(col, lin);
}
void LCD_print_float_justified(float value, byte col, byte lin) //print a floating point value between -999.99 and 999.99 right justified
{
if (value >= 0) col++; //value is positive so move the LCD column 1 position on right
if (abs(value) < 10) col += 2; //value has 1 digit + 2 decimals
if (abs((value) >= 10) && (abs(value) < 100)) col += 1; //value has 2 digits + 2 decimals
// lcd.setCursor(col, lin);
lcd.print(value, 2);
lcd.setCursor(col, lin);
}
void LCD_print_unsignedint(unsigned int value) //print an unsigned int number on the current position of the LCD with a "0000" mask.
{
if (value < 10) {
lcd.print("000");
lcd.print(value);
}
if ((value >= 10) && (value < 100)) {
lcd.print("00");
lcd.print(value);
}
if ((value >= 100) && (value < 1000)) {
lcd.print("0");
lcd.print(value);
}
if ((value >= 1000) && (value < 10000)) lcd.print(value);
}
float Change_float(float current_value, byte col, byte lin)
{
boolean stay = true;
boolean exit = false;
float prev_value = current_value;
float original_value = current_value;
LCD_print_float(current_value, col, lin);
while (stay)
{
if (digitalRead(ESC) == PRESSED) {
stay = false;
exit = true;
}
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) stay = false;
if (digitalRead(RESET) == PRESSED) current_value = 0.00;
if ((analogRead(ANALOG_Y) <= LOW_TOL) && (analogRead(ANALOG_Y) > LOW_1)) {
current_value -= 0.01;
delay (300);
}
if ((analogRead(ANALOG_Y) <= LOW_1) && (analogRead(ANALOG_Y) > LOW_2)) {
current_value -= 0.01;
delay (100);
}
if ((analogRead(ANALOG_Y) <= LOW_2) && (analogRead(ANALOG_Y) > LOW_3)) {
current_value -= 0.1;
delay (100);
}
if (analogRead(ANALOG_Y) <= LOW_3) {
current_value -= 1;
delay (100);
}
if ((analogRead(ANALOG_Y) >= HIGH_TOL) && (analogRead(ANALOG_Y) < HIGH_1)) {
current_value += 0.01;
delay(300);
}
if ((analogRead(ANALOG_Y) >= HIGH_1) && (analogRead(ANALOG_Y) < HIGH_2)) {
current_value += 0.01;
delay(100);
}
if ((analogRead(ANALOG_Y) >= HIGH_2) && (analogRead(ANALOG_Y) < HIGH_3)) {
current_value += 0.1;
delay (100);
}
if (analogRead(ANALOG_Y) >= HIGH_3) {
current_value += 1;
delay (100);
}
if (current_value != prev_value) {
LCD_print_float(current_value, col, lin);
prev_value = current_value;
}
}
delay(200);
if (exit) {
LCD_print_float(original_value, col, lin);
return original_value;
}
else {
LCD_print_float(current_value, col, lin);
return current_value;
}
}
void AvanzamentoLibero()
{
boolean stay = true;
boolean InnerStay = true;
boolean RUN = false;
boolean Update = true;
unsigned int NORTON_feed; //current FEED value with 1 turn of screw/Feedbar (cent/turn)
unsigned int MaxFeed = 0; //Max Feed speed in mm/min
unsigned int MandrelSpeed = 0; //current speed of the mandrel in RPM; used to calculate teh feedspeed while using cutting velocity in cent/turn
float Current_pos = 0.00; //Current position of the carriage/cross-slide
unsigned long StepperSpeed = 0; //Current speed of the stepper motor (RPM)
int FeedSpeed = 0; //feed speed in mm/min
if (CarroTrasv) NORTON_feed = av_carro[NORTON_gearbox]; //assign the value of the Feed given from the NORTON position and the carriage or cross-slide selected option (valid only for lathes with cross-slide feed gear)
else NORTON_feed = av_trasv[NORTON_gearbox];
SingleStepFeed = (((float)NORTON_feed / 100) / (float)one_turn_screw_steps); //calculates the feed given by a single step of the stepper motor
Current_pos = (float)absolute_steps * SingleStepFeed; //calculate the current position
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[2]))); lcd.setCursor(2, 0); lcd.print(buff); //avanzam. libero
if (mm_min) strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[19]))); // mm/min
else strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[22]))); //cent/g
lcd.setCursor(4, 2); lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[20]))); lcd.setCursor(12, 2); lcd.print(buff); // RPM
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[11]))); lcd.setCursor(0, 3); lcd.print(buff); // SEL:start
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[10]))); lcd.setCursor(12, 3); lcd.print(buff); // ESC:fine
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[13]))); lcd.setCursor(0, 1); lcd.print(buff); // print <===
LCD_print_float(Current_pos, 6, 1); // print the current position
lcd.setCursor(16, 2); LCD_print_unsignedint(GetSpeed(100));
SetPWM();
StopTimer1();
FeedSpeed = 0;
digitalWrite(DIR, CW);
Direction = CW;
while (stay) // MAIN CYCLE
{
while ((RUN == false) && (InnerStay)) // CYCLE WHEN THE FEED IS NOT RUNNING
{
delay (200);
if (digitalRead(ESC) == PRESSED) {stay = false; InnerStay = false; beepON(); delay(100);}
if (digitalRead(RESET) == PRESSED) {absolute_steps = 0; Current_pos = absolute_steps * SingleStepFeed; LCD_print_float(Current_pos, 6, 1);}
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) {Update = true; RUN = true; strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[12]))); lcd.setCursor(0, 3); lcd.print(buff);beepON(); delay(100);} // SEL:stop
MandrelSpeed = GetSpeed(200);
lcd.setCursor(16, 2);
LCD_print_unsignedint(MandrelSpeed);
if (mm_min) MaxFeed = (NORTON_feed * MaxStepperSpeed) / 100; //calculate the max feed speed starting from the max stepper speed (mm/min)
else MaxFeed = (unsigned int)((float)NORTON_feed * (float)MaxStepperSpeed) / MandrelSpeed; //calculate the max feed based on mandrel speed and max stepper motore speede (cent/g)
if (FeedSpeed > MaxFeed){ FeedSpeed = MaxFeed; lcd.setCursor(0, 2); lcd.print(FeedSpeed); lcd.print(" "); Update = true;} //if the current FeedSpeed is higher than the max, limit and print it
if (analogRead(ANALOG_Y) < LOW_TOL) {
FeedSpeed --;
if (FeedSpeed < 0) FeedSpeed = 0;
lcd.setCursor(0, 2); lcd.print(FeedSpeed); lcd.print(" ");
Update = true;
delay(30);
}
if (analogRead(ANALOG_Y) > HIGH_TOL) {
FeedSpeed ++;
if (FeedSpeed > MaxFeed) FeedSpeed = MaxFeed;
lcd.setCursor(0, 2); lcd.print(FeedSpeed); lcd.print(" ");
Update = true;
delay(30);
}
if ((analogRead(ANALOG_X) < LOW_1)) {
digitalWrite(DIR, CW);
Direction = CW;
lcd.setCursor(14, 1);
lcd.print(" ");
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[13]))); // print <===
lcd.setCursor(0, 1);
lcd.print(buff);
Update = true;
}
if ((analogRead(ANALOG_X) > HIGH_1)) {
digitalWrite(DIR, CCW);
Direction = CCW;
lcd.setCursor(0, 1);
lcd.print(" ");
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[14]))); // print ===>
lcd.setCursor(14, 1);
lcd.print(buff);
Update = true;
}
}
while (RUN) //CYCLE WHEN THE FEED IS RUNNING
{
if (digitalRead(ESC) == PRESSED) {StopTimer1(); Speed = 0; stay = false; InnerStay = false; beepON(); RUN = false; delay(100);}
if (digitalRead(RESET) == PRESSED) {absolute_steps = 0; beepON();}
if ((digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) && (analogRead(ANALOG_X) > LOW_TOL) && (analogRead(ANALOG_X) < HIGH_TOL)){ //IF SEL is pressed and the joy is not "X" moved
StopTimer1(); Speed = 0; // Stop timer 1 and reset stepper speed
RUN = false; // clear the flag to come out from the RUN cycle
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[11]))); // print SEL:start
lcd.setCursor(0,3);
lcd.print(buff);
beepON();
delay(150);
}
if (analogRead(ANALOG_Y) < LOW_TOL) {
FeedSpeed --;
if (FeedSpeed < 0) FeedSpeed = 0;
lcd.setCursor(0, 2); lcd.print(FeedSpeed); lcd.print(" ");
Update = true;
delay(50);
}
if (analogRead(ANALOG_Y) > HIGH_TOL) {
FeedSpeed ++;
if (FeedSpeed > MaxFeed) FeedSpeed = MaxFeed;
lcd.setCursor(0, 2); lcd.print(FeedSpeed); lcd.print(" ");
Update = true;
delay(50);
}
if ((analogRead(ANALOG_X) < LOW_1)) {
if (Direction == CCW) {
StepperRunToSpeed(0);
digitalWrite(DIR, CW);
Direction = CW;
lcd.setCursor(14, 1);
lcd.print(" ");
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[13]))); // print <===
lcd.setCursor(0, 1);
lcd.print(buff);
Update = true;
}
else{
if ((analogRead(ANALOG_X) < LOW_3) && (digitalRead(SEL) == PRESSED)) StepperRunToSpeed(MaxStepperSpeed); //move direction and press SEL to go fast
}
}
if ((analogRead(ANALOG_X) > HIGH_1)) {
if (Direction == CW) {
StepperRunToSpeed(0);
digitalWrite(DIR, CCW);
Direction = CCW;
lcd.setCursor(0, 1);
lcd.print(" ");
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[14]))); // print ===>
lcd.setCursor(14, 1);
lcd.print(buff);
Update = true;
}
else{
if ((analogRead(ANALOG_X) > HIGH_3) && (digitalRead(SEL) == PRESSED)) StepperRunToSpeed(MaxStepperSpeed); //move direction and press SEL to go fast
}
}
if (Update){
if (mm_min) StepperSpeed = (unsigned long)((float)FeedSpeed / ((float)NORTON_feed / 100));
else StepperSpeed = (unsigned long)(MandrelSpeed/((float)NORTON_feed / (float)FeedSpeed));
StepperRunToSpeed(StepperSpeed);
Update = false;
}
Current_pos = absolute_steps * SingleStepFeed;
LCD_print_float(Current_pos, 6, 1);
}
}
ClearPWM();
Speed = 0;
delay(300);
}
void AvanzamentoVincolato()
{
boolean stay = true;
boolean innerstay = true;
boolean RUN = false;
boolean Update = true;
boolean left = true; //used to check if the left direction is free (became flase when the Current_pos hits the left limit
boolean right = true; //used to check if the right direction is free (became flase when the Current_pos hits the right limit
byte CursorPos = 0; //0: SX value change; 1:DX Value change: 2:START
byte OLD_CursorPos = 3; //Valore CursorPos ciclo precedente
float SX_pos = 0.00; //left limit position
float DX_pos = 0.00; //right limit position
float Current_pos = 0.00; //Current position of the carriage/cross-slide
int FeedSpeed = 0; //feed speed in mm/min
unsigned int MaxFeed = 0; //Max Feed speed in mm/min
unsigned int MandrelSpeed = 0; //current speed of the mandrel in RPM; used to calculate teh feedspeed while using cutting velocity in cent/turn
unsigned long StepperSpeed;
unsigned int NORTON_feed; //current FEED value with 1 turn of screw/Feedbar (cent/turn)
if (CarroTrasv) NORTON_feed = av_carro[NORTON_gearbox]; //assign the value of the Feed given from the NORTON position and the carriage or cross-slide selected option (valid only for lathes with cross-slide feed gear)
else NORTON_feed = av_trasv[NORTON_gearbox];
SingleStepFeed = (((float)NORTON_feed / 100) / (float)one_turn_screw_steps); //calculates the feed given by a single step of the stepper motor
Current_pos = (float)absolute_steps * SingleStepFeed;
SX_pos = Current_pos;
DX_pos = Current_pos - 1.50;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[16]))); lcd.setCursor(4, 0); lcd.print(buff); // Pos
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[18]))); lcd.setCursor(9, 1); lcd.print(buff); // ==
if (mm_min) strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[19]))); // mm/min
else strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[22]))); //cent/g
lcd.setCursor(4, 2); lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[20]))); lcd.setCursor(12, 2); lcd.print(buff); // RPM
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[21]))); lcd.setCursor(0, 3); lcd.print(buff); // SEL:camnbia
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[10]))); lcd.setCursor(12, 3); lcd.print(buff); // ESC:fine
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[17]))); lcd.setCursor(18, 0); lcd.print(buff); // mm
LCD_print_float(SX_pos, 0, 1);
LCD_print_float(DX_pos, 13, 1);
LCD_print_float(Current_pos, 8, 0);
digitalWrite(DIR, CW); //set standard feed in CW direction: right to left
lcd.setCursor(8, 1);
lcd.print("<"); //print the current feed direction
Direction = CW;
lcd.setCursor(0,2);
lcd.print("0");
SetPWM();
StopTimer1();
FeedSpeed = 0;
while (stay) // MAIN CYCLE
{
while ((RUN == false) && (innerstay)) // CYCLE WHEN THE FEED IS NOT RUNNING
{
if (digitalRead(ESC) == PRESSED) {innerstay = false;beepON();stay = false;delay(200);}
if (digitalRead(RESET) == PRESSED){absolute_steps = 0; Current_pos = absolute_steps * SingleStepFeed; LCD_print_float(Current_pos, 8, 0); beepON(); delay(500);}
if (analogRead(ANALOG_X) < LOW_3){
if(CursorPos > 0) CursorPos--;
if (CursorPos == 2) {strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[11])));delay(500);} else {strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[21])));delay(500);} ;
lcd.noBlink();
lcd.setCursor(0, 3);
lcd.print(buff);
delay(200);
}
if (analogRead(ANALOG_X) > HIGH_3){
if(CursorPos < 2) CursorPos++;
if (CursorPos == 2) {strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[11])));delay(500);} else {strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[21])));delay(500);} ;
lcd.noBlink();
lcd.setCursor(0, 3);
lcd.print(buff);
delay(200);
}
switch (CursorPos) {
case 0: lcd.blink(); lcd.setCursor(0,1);
if (CursorPos != OLD_CursorPos && OLD_CursorPos != 3 )
beepON();
;
break;
case 1: lcd.blink(); lcd.setCursor(13,1);
if (CursorPos != OLD_CursorPos && OLD_CursorPos != 3 )
beepON();
;
break;
case 2: lcd.setCursor(16, 2); MandrelSpeed = GetSpeed(100); LCD_print_unsignedint(MandrelSpeed);
if (CursorPos != OLD_CursorPos && OLD_CursorPos != 3 )
beepON();
;
break;
}
;
OLD_CursorPos = CursorPos;
if (mm_min) MaxFeed = (NORTON_feed * MaxStepperSpeed) / 100; //calculate the max feed speed starting from the max stepper speed (mm/min)
else MaxFeed = (unsigned int)((float)NORTON_feed * (float)MaxStepperSpeed) / MandrelSpeed; //calculate the max feed based on mandrel speed and max stepper motore speede (cent/g)
if (FeedSpeed > MaxFeed){ FeedSpeed = MaxFeed; lcd.setCursor(0, 2); lcd.print(FeedSpeed); lcd.print(" "); Update = true;} //if the current FeedSpeed is higher than the max, limit and print it
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) {
delay(200);
beepON();
delay(500);
switch (CursorPos) {
case 0: lcd.noBlink(); lcd.cursor(); SX_pos = Change_float(SX_pos, 0,1); lcd.noCursor(); lcd.blink(); break;
case 1: lcd.noBlink(); lcd.cursor(); DX_pos = Change_float(DX_pos,13,1); lcd.noCursor(); lcd.blink(); break;
case 2: RUN = true; // set the flag to come in the RUN cycle
lcd.noBlink();
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[12]))); // print SEL:stop
lcd.setCursor(0,3);
lcd.print(buff);
Update = true;
delay(300);
break;
}
}
}
while ((RUN) && (innerstay)) //CYCLE WHEN THE FEED IS RUNNING
{
if (digitalRead(ESC) == PRESSED) {innerstay = false; stay = false; delay(300);}
if (digitalRead(RESET) == PRESSED) absolute_steps = 0;
if ((digitalRead(SEL) == PRESSED ) && (analogRead(ANALOG_X) > LOW_TOL) && (analogRead(ANALOG_X) < HIGH_TOL)){ //IF SEL is pressed and the joy is not "X" moved
StopTimer1(); Speed = 0; // Stop timer 1 and reset stepper speed
RUN = false; // clear the flag to come out from the RUN cycle
strcpy_P(buff, (char*)pgm_read_word(&(MenuAvanzamento[11]))); // print SEL:start
lcd.setCursor(0,3);
lcd.print(buff);
CursorPos = 2; //positiooning the cursor on the START selection
beepON();
delay(300);
}
if (analogRead(ANALOG_Y) < LOW_TOL) {
FeedSpeed --;
if (FeedSpeed < 0) FeedSpeed = 0;
lcd.setCursor(0, 2); lcd.print(FeedSpeed); lcd.print(" ");
if (((left) && (Direction == CW)) || ((right) && (Direction == CCW))) Update = true;
delay(30);
}
if (analogRead(ANALOG_Y) > HIGH_TOL) {
FeedSpeed ++;
if (FeedSpeed > MaxFeed) FeedSpeed = MaxFeed;
lcd.setCursor(0, 2); lcd.print(FeedSpeed); lcd.print(" ");
if (((left) && (Direction == CW)) || ((right) && (Direction == CCW))) Update = true;
delay(30);
}
if ((analogRead(ANALOG_X) < LOW_1) && (left)) {
if (Direction == CCW) {
StepperRunToSpeed(0);
digitalWrite(DIR, CW);
Direction = CW;
lcd.setCursor(11, 1);
lcd.print(" ");
lcd.setCursor(8, 1);
lcd.print("<");
beepON();
Update = true;
}
else{
if ((analogRead(ANALOG_X) < LOW_3) && (digitalRead(SEL) == PRESSED)) StepperRunToSpeed(MaxStepperSpeed); //move direction and press SEL to go fast
}
}
if ((analogRead(ANALOG_X) > HIGH_1) && (right)) {
if (Direction == CW) {
StepperRunToSpeed(0);
digitalWrite(DIR, CCW);
Direction = CCW;
lcd.setCursor(8, 1);
lcd.print(" ");
lcd.setCursor(11, 1);
lcd.print(">");
beepON();
Update = true;
}
else{
if ((analogRead(ANALOG_X) > HIGH_3) && (digitalRead(SEL) == PRESSED)) StepperRunToSpeed(MaxStepperSpeed); //move direction and press SEL to go fast
}
}
if (Update){
if (mm_min) StepperSpeed = (unsigned long)((float)FeedSpeed / ((float)NORTON_feed / 100));
else StepperSpeed = (unsigned long)(MandrelSpeed/((float)NORTON_feed / (float)FeedSpeed));
StepperRunToSpeed(StepperSpeed);
Update = false;
}
Current_pos = absolute_steps * SingleStepFeed;
if ((Current_pos >= SX_pos) && (Direction == CW)) {StopTimer1(); Speed = 0; left = false;} else left = true;
if ((Current_pos <= DX_pos) && (Direction == CCW)) {StopTimer1(); Speed = 0; right = false;} else right = true;
LCD_print_float(Current_pos, 8, 0);
}
}
ClearPWM();
Speed = 0;
lcd.noBlink();
}
void Avanzamento()
{
boolean resta = true;
byte Opzione;
while (resta)
{
Opzione = AvanzamentoMenu();
switch (Opzione) {
case 1: LongTrasv(); break; //long/trasv
case 2: AvanzamentoLibero(); break; //Avanzamento Libero
case 3: AvanzamentoVincolato(); break; //Avanzamnto Vincolato
case 4: ImpostaNORTON(); break; //Imposta NORTON
case 5: ImpostaModalitaAvanzamento(); break; //Imposta la modalita' di avanzamento
case 6: break; //VUOTO
case 7: resta = false; break;
}
}
}
void InterruptEncoderAvanzamento() //this is the Interrupt Service Routine which manage the encoder inputs while feeding (250 step/turn)
//activate this interrupt only with RISING on Encoder A channel (INT0)
{
absolute_encoder_steps ++;
}
unsigned int GetSpeed (unsigned long frame)
{
boolean stay = true; //boolean to determine if stay or not in the reading cycle
unsigned int MandrelSpeed = 0; //speed of the mandrel calculated
unsigned long prev_millis = 0; //used to store the millis alue for the speed count routine
const float speed_multiplier = ((1000/(float)frame )/((float)one_turn_mandrel_steps/4))*60; //this is the multiplier used to calculate the speed
// this is the multiplier used by to calculate the speed
// one_turn_mandrel_steps is the total number of encoder steps per every single mandrel turn
// the interrupt used in this routine is triggered only on one edge of one channel of the encoder, therefore it taked only 1 step every 4.
// 1000/(float)interval: 1000msec (1sec) divided by the interval gives the ratio of the interval with respect to 1 second
// (float)one_turn_mandrel_steps/4): the number of steps in one turn are divided by 4 (due to interrupt explained above)
// (1000/(float)interval)/((float)one_turn_mandrel_steps/4): this therefore gives the number of turn per seconds, which are then multiplied by 60 to get the speed in RPM
absolute_encoder_steps = 0;
prev_millis = millis();
attachInterrupt(0, InterruptEncoderAvanzamento, RISING);
while (stay)
{
if (millis()-prev_millis >= frame)
{
detachInterrupt(0);
MandrelSpeed = (unsigned int)((float)absolute_encoder_steps*speed_multiplier); //calculates the speed
// CALCULATION OF SPEED
// this routine calculates how many steps are done by the mandrel during "inteval" (in milliseconds)
// the number of steps are multiplied by the "speed_multiplier" which is calculated above in the constant definition
// the result is the exact speed in round per minute
absolute_encoder_steps = 0;
stay = false;
}
}
detachInterrupt(0);
return MandrelSpeed;
}
/* EEPROM factory defaults and parameters to be stored
*
* PARAMETER |TYPE |SIZE |STARTING LOCATION |FACTORY DEFAULT
* one_turn_mandrel_steps |unsigned int |2 |0 |1000
* screw_pitch |float |4 |2 |3.175
* one_turn_screw_steps |unsigned int |2 |6 |400
* AccelerationDelay |unsigned int |2 |8 |500
* DecelerationDelay |unsigned int |2 |10 |500
* av_carro_1 |unsigned int |2 |12 |50
* av_trasv_1 |unsigned int |2 |14 |7
* av_carro_2 |unsigned int |2 |16 |30
* av_trasv_2 |unsigned int |2 |18 |5
* av_carro_3 |unsigned int |2 |20 |20
* av_trasv_3 |unsigned int |2 |22 |3
* av_carro_4 |unsigned int |2 |24 |10
* av_trasv_4 |unsigned int |2 |26 |2
* CW |boolean |1 |28 |HIGH
* CCW |boolean |1 |29 |LOW
* offset_filetto |unsigned int |2 |30 |400
* NORTON |byte |1 |32 |1
* MaxStepperSpeed |unsigned long |4 |33 |500
* FREE | | |37 |
*/
#define one_turn_mandrel_steps_default 1600 // 400x4
#define screw_pitch_default 1.500
#define one_turn_screw_steps_default 2400 // microstepping a 800 e riduzione 1:3
#define AccelerationDelay_default 800
#define DecelerationDelay_default 800
#define CW_default LOW
#define CCW_default HIGH
#define av_carro_1_default 300 //avanzamento carro in centesimi/giro per rapporto semiNorton 1
#define av_trasv_1_default 300 //avanzamento trasversale in centesimi/giro per rapporto semiNorton 1
#define av_carro_2_default 50 //avanzamento carro in centesimi/giro per rapporto semiNorton 2
#define av_trasv_2_default 50 //avanzamento trasversale in centesimi/giro per rapporto semiNorton 2
#define av_carro_3_default 50 //avanzamento carro in centesimi/giro per rapporto semiNorton 3
#define av_trasv_3_default 50 //avanzamento trasversale in centesimi/giro per rapporto semiNorton 3
#define av_carro_4_default 50 //avanzamento carro in centesimi/giro per rapporto semiNorton 4
#define av_trasv_4_default 50 //avanzamento trasversale in centesimi/giro per rapporto semiNorton 4
#define offset_filetto_default 400 //offset per inizio filettatura (in passi stepper vite) ---59 step---
#define norton_default 4 //posizione scatola NORTON avanzamenti (av_carro_1 e av_trasv_1) - 4 corrisponde ad OFF
#define MaxStepperSpeed_default 500 //Max speed allowed to the stepper motor
void FactoryDefaultEEPROM() //rester the FACTORY DEFAULT values in the EEPROM
{
unsigned int UI;
float F;
byte B;
boolean BOO;
String C;
long L;
unsigned long UL;
//RESTORES FACTORY DEFAULT PARAMETERS IN EEPROM
UI = one_turn_mandrel_steps_default; //
EEPROM.put(0, UI); //number of mandrel encoder steps per revolution
F = screw_pitch_default; //
EEPROM.put(2, F); //lead screw pitch
UI = one_turn_screw_steps_default; //
EEPROM.put(6, UI); //number of lead screw steps (stepper motor steps per revolution)
UI = AccelerationDelay_default; //
EEPROM.put(8, UI); //stepper motor acceleration delay
UI = DecelerationDelay_default; //
EEPROM.put(10, UI); //stepper motor deceleration delay
//CARRIAGE AND CROSS SLIDE FEEDS FOR EVERY NORTON GEARBOX RATIO (4 RATIOS IN TOTAL)
UI = av_carro_1_default;
EEPROM.put(12, UI);
UI = av_trasv_1_default;
EEPROM.put(14, UI);
UI = av_carro_2_default;
EEPROM.put(16, UI);
UI = av_trasv_2_default;
EEPROM.put(18, UI);
UI = av_carro_3_default;
EEPROM.put(20, UI);
UI = av_trasv_3_default;
EEPROM.put(22, UI);
UI = av_carro_4_default;
EEPROM.put(24, UI);
UI = av_trasv_4_default;
EEPROM.put(26, UI);
BOO = CW_default; //
EEPROM.put(28, BOO); //
BOO = CCW_default; //STEPPER MOTOR DIRECTION
EEPROM.put(29, BOO); //
UI = offset_filetto_default; //
EEPROM.put(30, UI); //offset for thread start
B = norton_default; //
EEPROM.put(32,B); //NORTON GEARBOX POSITION
UL = MaxStepperSpeed_default; //
EEPROM.put(33,UL); //Max stepper speed
}
void WriteToEEPROM() //Writes the current value to the EEPROM if they've been modified by the user
//Factory default setting can be reset with the FactoryDefaultEEPROM function
{
EEPROM.put(0, one_turn_mandrel_steps); //number of encoder steps per mandrel revolution (unsigned int)
EEPROM.put(2, screw_pitch); //leadscrew pitch value (float decimal)
EEPROM.put(6, one_turn_screw_steps); //number of STEPPER steps per leadscrew revolution (unsigned int)
EEPROM.put(8, AccelerationDelay); //delay value for the STEPPER acceleration in microseconds (unsigned int)
EEPROM.put(10, DecelerationDelay); //delay value for the STEPPER deceleration in microseconds (unsigned int)
//this cycle takes the 4 values of the feed (both carriage and cross-slide) from the 2 arrays (av_carro and av_trasv)
//and put them in the EEPROM
byte address = 12;
for (byte i = 0; i < 4; i++)
{
EEPROM.put(address, av_carro[i]);
address += 2;
EEPROM.put(address, av_trasv[i]);
address += 2;
}
EEPROM.put(28, CW); //ClockWise direction of the leadscrew stepper (boolean)
EEPROM.put(29, CCW); //CounterClockWise direction of the leadscrew stepper (boolean)
EEPROM.put(30, thread_offset_steps); //offset for thread start
if ( NORTON_gearbox == 0 && NORTON == false ) { NORTON_gearbox = 4; }
EEPROM.put(32,NORTON_gearbox); //NORTON GEARBOX POSITION
EEPROM.put(33,MaxStepperSpeed); //Max stepper speed limit
}
void LoadFromEEPROM() //Loads the parameters from the EEPROM and put them in the proper variables
{
EEPROM.get(0, one_turn_mandrel_steps); //number of encoder steps per mandrel revolution (unsigned int)
EEPROM.get(2, screw_pitch); //leadscrew pitch value (float decimal)
EEPROM.get(6, one_turn_screw_steps); //number of STEPPER steps per leadscrew revolution (unsigned int)
EEPROM.get(8, AccelerationDelay); //delay value for the STEPPER acceleration in microseconds (unsigned int)
EEPROM.get(10, DecelerationDelay); //delay value for the STEPPER deceleration in microseconds (unsigned int)
//this cycle takes the 4 values of the feed (both carriage and cross-slide) and put them in 2 arrays
//one for the carriage (av_carro) and the other for the cross-slide(av_trasv)
byte address = 12;
for (byte i = 0; i < 4; i++)
{
EEPROM.get(address, av_carro[i]);
address += 2;
EEPROM.get(address, av_trasv[i]);
address += 2;
}
EEPROM.get(28, CW); //ClockWise direction of the leadscrew stepper (boolean)
EEPROM.get(29, CCW); //CounterClockWise direction of the leadscrew stepper (boolean)
EEPROM.get(30, thread_offset_steps); //offset for thread start
EEPROM.get(32, NORTON_gearbox); //NORTON GEARBOX POSITION
if ( NORTON_gearbox == 4 ) { NORTON_gearbox = 0; NORTON = false;}
EEPROM.get(33, MaxStepperSpeed); //Max stepper speed limit
single_step_pitch = screw_pitch/one_turn_screw_steps; //calculates the linear movement (in mm) of the carriage for a single step of the stepper motor
}
const char FIL0[] PROGMEM = "imposta passo mm";
const char FIL1[] PROGMEM = "imposta passo TPI";
const char FIL2[] PROGMEM = "sviluppo";
const char FIL3[] PROGMEM = "fil. a misura";
const char FIL4[] PROGMEM = "vincolo meccanico";
const char FIL5[] PROGMEM = "destro ";
const char FIL6[] PROGMEM = "sinistro";
const char FIL7[] PROGMEM = "VINCOLATO";
const char FIL8[] PROGMEM = "passo: ";
const char FIL9[] PROGMEM = "SEL: blocca";
const char FIL10[] PROGMEM = "SEL+RESET: ritorna ";
const char FIL11[] PROGMEM = "SEL:start ESC:esci";
const char FIL12[] PROGMEM = "ESC:esci";
const char FIL13[] PROGMEM = "dist:";
const char FIL14[] PROGMEM = "imposta corsa";
const char FIL15[] PROGMEM = " ** FILETTATURA ** ";
const char FIL16[] PROGMEM = "aggiusta posizione";
const char FIL17[] PROGMEM = " < -------------- >";
const char* const MenuFilettatura[] PROGMEM = {FIL0, FIL1, FIL2, FIL3, FIL4, FIL5,
FIL6, FIL7, FIL8, FIL9, FIL10, FIL11, FIL12, FIL13, FIL14, FIL15, FIL16, FIL17};
void CreaSequenza(float passo) // this function generates the array with the step configuration to build the screw with the
// reqeusted pitch "passo" passed in the parameter
{
// local variable declaration
int mandrel_steps_counter = 0; // variable temporanea da eliminare che totalizza il numero di passi per verificare che vengano effetuati tuti i passi dell'encoder mandrino
float real_absolute_ratio = 0; //this variable is used to calculate the ratio between mandrel steps and screw steps
unsigned long long_progressive_ratio = 0; //this variable is used to store the progrerssive ratio in long integer format including the remainder of the previous step
float real_progressive_ratio = 0.0; //this variable is used to store the progrerssive ratio in float format including the remainder of the previous step
float decimal_remainder = 0; //this variable is used to store the decimal remainder of the calculated ratio
byte actual_integer_steps = 0; //this variable is used to store the interger part of the number of steps, separed from the decimal remainder
unsigned int matched_mandrel_steps = one_turn_mandrel_steps; //this is the calculated mandrel steps in order to get a "clear" ratio to compansate system nonlinearity and inches to mm conversion
unsigned int progressive_mandrel_steps; //this is used to calculate the matched mandrel steps
float progressive_pitch; //this is used to calculate the reduced pitch according to the calculated matched mandrel steps
unsigned long best_remainder = 99999; //this is the remainder to check when it reaches 0
unsigned long int_ratio;
unsigned long integer_remainder;
float matched_pitch = passo;
boolean PLusOne = false;
// end local variable declaration
for (pointer = 0; pointer < MaxSteps; pointer++) //this cycle initializes the "sequenza" array to 0
{
sequenza[pointer] = 0;
}
for (progressive_mandrel_steps = one_turn_mandrel_steps; progressive_mandrel_steps > 10; progressive_mandrel_steps--)
{
progressive_pitch = (passo/one_turn_mandrel_steps)*progressive_mandrel_steps; //progressive pitch calculated on the current mandrel steps
long_progressive_ratio = (progressive_pitch/single_step_pitch)*1000000; //calculates the number of screw steps required to perform the threadmultiplied by 1000000 to avoid float
integer_remainder = long_progressive_ratio % 1000000;
actual_integer_steps = long_progressive_ratio/1000000; //extract the integer part of the calculated steps
if (integer_remainder > 500000) integer_remainder = 1000000 - integer_remainder; //extract the decimal part of the calculated steps
if (integer_remainder < 500) integer_remainder = 0; //round the last 2.5 digits of the calculated steps
if (integer_remainder < best_remainder){ //if the current remainder il lower than the current best...
best_remainder = integer_remainder; //make the actual the current best
matched_mandrel_steps = progressive_mandrel_steps; //save the current progressive steps
matched_pitch = progressive_pitch; //Matched_pitch is the pitch of the screw to be created reduced by the angular factor of the partial mandrel revolution
}
if (best_remainder == 0) break; //if the best remainder is already 0 we have found the best combination: exit the cycle immediately
}
numero_passi = round(matched_pitch / single_step_pitch); //numero di passi della vite madre da eseguire per ottenere il passo ridotto (matched_pitch)
real_absolute_ratio = float(matched_mandrel_steps) / numero_passi; //rapporto tra passi mandrino e passi vite calcolato con il passo angolare ridotto
mandrel_steps_counter = 0; //azzero il contatore passi mandrino
for (pointer = 0; pointer < numero_passi; pointer++) //ciclo che carica i valori dei passi nell'array che ha tante celle quanti sono i passi vite da eseguire
{
real_progressive_ratio = real_absolute_ratio + decimal_remainder; //il rapporto progressivo è uguale al rapporto assoluto con aggiunto il resto decimale del rapporto precedente
actual_integer_steps = int(real_progressive_ratio); //identifico quanti passi interi devo eseguire prendendo la sola parte intera del rapporto progressivo
decimal_remainder = real_progressive_ratio - actual_integer_steps; //estraggo la parte deimale dal rapporto progressivo
mandrel_steps_counter = mandrel_steps_counter + actual_integer_steps; //incremento il contatore passi mandrino aggiungendo i passi interi appena calcolati
sequenza[pointer] = actual_integer_steps; //inserisco il valore dei passi interi appena calcolati nella cella corrente dell'array
}
pointer--; //decremento il puntatore e mi sposto sull'ultimo valore dell'array
sequenza[pointer] = sequenza[pointer] + (matched_mandrel_steps - mandrel_steps_counter); //aggiusto il valore dell'ultima cella in modo da avere il totale passi mandrino corretto
} //END CreaSequenza
void InterruptEncoderFilettatura() //this is the Interrupt Service Routine which manage the encoder inputs while threading
{
static byte prev_encoder; // this variable stores the value of the input port register of the previous encoder state
byte port; // this variable stores the value of the input port register of the current encoder state
port = PIND & B00001100; //reads input pins 2 and 3 of the Arduino uno PORTD D and puts the binary image on the "port" byte variable
port |= prev_encoder; //makes the logic "OR" between the current "port" byte and the previous
steps = steps + encoder[port]; //increment/decrement the encoder couter according to the previous and current encoder values
absolute_encoder_steps = absolute_encoder_steps + encoder[port]; //increment the absolute encoder counter
port >>= 2; //shift the port image by 2 bits on right
prev_encoder = port; //assign the current port image to the "prev_encoder" byte by keeping just the bits n. 2 and 3
step_flag = true;
} //END of the Interrupt Service Routine
char FilettaturaMenu()
{
static char Current;
char previous = 4;
char next = 2;
boolean stay = true;
boolean innerstay = true;
lcd.clear();
lcd.setCursor(0, 2);
lcd.print(">");
lcd.setCursor(19, 2);
lcd.print("<");
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[15])));
lcd.setCursor(0, 0);
lcd.print(buff);
while (stay)
{
switch (Current) {
case -1: Current = 4; break;
case 5: Current = 0; break;
}
previous = Current - 1;
next = Current + 1;
if (previous == -1)
previous = 4;
if (next == 5)
next = 0;
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[previous])));
lcd.setCursor(1, 1);
lcd.print(" ");
lcd.setCursor(1, 1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[Current])));
lcd.setCursor(1, 2);
lcd.print(" ");
lcd.setCursor(1, 2);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[next])));
lcd.setCursor(1, 3);
lcd.print(" ");
lcd.setCursor(1, 3);
lcd.print(buff);
innerstay = true;
while (innerstay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED ) {
stay = false;
innerstay = false;
delay(300);
}
if (analogRead(ANALOG_Y) < LOW_1) {
Current -= 1;delay(300);
innerstay = false;
beepON();
}
if (analogRead(ANALOG_Y) > HIGH_1) {
Current += 1;
innerstay = false;
beepON();
}
if (digitalRead(ESC) == PRESSED) {
Current = 5;
stay = false;
innerstay = false;
}
}
delay (300);
}
return Current;
}
void ImpostaPassoPollici() //this set the required thread pitch in inches and creates the valiue array for the threading
{
float MaxPasso = (screw_pitch / one_turn_screw_steps) * MaxSteps;
float thread_pitch_mm;
boolean stay = true;
byte MaxTPI = 1/(MaxPasso/25.4);
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[15])));
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[1])));
lcd.setCursor(1, 1);
lcd.print(buff);
lcd.setCursor(1, 3);
lcd.print("(max) "); lcd.print(MaxTPI); lcd.print(" TPI");
lcd.setCursor(6, 2);
lcd.print("TPI");
delay(200);
while (stay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) stay = false;
if (analogRead(ANALOG_Y) < LOW_3) TPI -= 1;
if (analogRead(ANALOG_Y) > HIGH_3) TPI += 1;
if (TPI < MaxTPI) TPI = MaxTPI;
if (TPI > 56) TPI = 56;
lcd.setCursor(1, 2);
lcd.print(TPI);
lcd.print(" ");
delay(150);
}
thread_pitch_mm = 25.4/(float)TPI;
Metric = false;
CreaSequenza(thread_pitch_mm);
}
void ImpostaPassoMetrico() //this set the required thread pitch in mm and creates the valiue array for the threading
{
float MaxPasso = (screw_pitch / one_turn_screw_steps) * MaxSteps;
boolean stay = true;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[15])));
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[0])));
lcd.setCursor(1, 1);
lcd.print(buff);
lcd.setCursor(1, 3);
lcd.print("(max) "); lcd.print(MaxPasso, 2); lcd.print(" mm");
lcd.setCursor(6, 2);
lcd.print("mm");
delay(200);
while (stay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) stay = false;
if ((analogRead(ANALOG_Y) < LOW_TOL) && (analogRead(ANALOG_Y) > LOW_3)) thread_pitch -= 0.05;
if (analogRead(ANALOG_Y) <= LOW_3) thread_pitch -= 0.1;
if ((analogRead(ANALOG_Y) > HIGH_TOL) && (analogRead(ANALOG_Y) < HIGH_3)) thread_pitch += 0.05;
if (analogRead(ANALOG_Y) >= HIGH_3) thread_pitch += 0.1;
if (thread_pitch > MaxPasso) thread_pitch = MaxPasso;
if (thread_pitch < 0.1) thread_pitch = 0.1;
lcd.setCursor(1, 2);
lcd.print(thread_pitch, 2);
delay(150);
}
Metric = true;
CreaSequenza(thread_pitch);
}
void Sviluppo() //set the threading direction (left-hand or right-hand)
{
boolean stay = true;
boolean innerStay = true;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[15])));
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[2])));
lcd.setCursor(1, 1);
lcd.print(buff);
while (stay)
{
if (sviluppo_filetto) strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[5]))); else strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[6])));
lcd.setCursor(1, 2);
lcd.print(buff);
innerStay = true;
while (innerStay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) {
innerStay = false;
stay = false;
}
if ((analogRead(ANALOG_Y) < LOW_1) | (analogRead(ANALOG_Y) > HIGH_1)) {
innerStay = false;
sviluppo_filetto = !(sviluppo_filetto);
delay(50);
}
}
delay(200);
}
}
void FilettaturaToPosition() //threading to a specific distance (in mm with 2 decimals)
{
boolean resta = true;
boolean pass_done = false;
boolean stay = true;
boolean esegui = true;
unsigned long ThreadSteps = 0;
unsigned int joy;
unsigned long stepperSpeed;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[15]))); //FILETTATURA
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[16]))); //aggiusta posizione
lcd.setCursor(1, 1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[17]))); //< -------------- >
lcd.setCursor(0, 2);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[11]))); //SEL:start ESC:esci
lcd.setCursor(0, 3);
lcd.print(buff);
SetPWM();
while ((stay) && (esegui))
{
joy = analogRead(ANALOG_X);
if (joy >= HIGH_TOL) { stepperSpeed = 50; Direction = CCW;}
if (joy <= LOW_TOL) { stepperSpeed = 50; Direction = CW;}
if ((joy >= LOW_TOL) && (joy <=HIGH_TOL)) stepperSpeed = 0;
if (digitalRead(DIR) != Direction) {if (Speed == 0) digitalWrite(DIR, Direction); delayMicroseconds(10);}
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) stay = false;
if (digitalRead(ESC) == PRESSED){ esegui = false; stay = false; delay(200);}
if (digitalRead(DIR) == Direction) StepperRunToSpeed(stepperSpeed); else StepperRunToSpeed(0);
delay (10);
}
StepperRunToSpeed(0); // be sure the stepper motor is stopped
delay(50);
ClearPWM();
if (esegui == false) return;
stay = true;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[15]))); //FILETTATURA
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[14]))); //IMPOSTA CORSA
lcd.setCursor(1, 1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[11]))); //SEL:start ESC:esci
lcd.setCursor(0, 3);
lcd.print(buff);
delay(200);
while ((stay) && (esegui))
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) stay = false;
if (digitalRead(ESC) == PRESSED){ esegui = false; stay = false;}
if ((analogRead(ANALOG_Y) < LOW_TOL) && (analogRead(ANALOG_Y) > LOW_3)) distance -= 0.1;
if (analogRead(ANALOG_Y) <= LOW_3) distance -= 1.0;
if ((analogRead(ANALOG_Y) > HIGH_TOL) && (analogRead(ANALOG_Y) < HIGH_3)) distance += 0.1;
if (analogRead(ANALOG_Y) >= HIGH_3) distance += 1.0;
if (distance < 0) distance = 0.0;
lcd.setCursor(1, 2);
if (distance > 0) {lcd.print(distance, 2); lcd.print(" mm");} else lcd.print ("OFF ");
delay(150);
}
if (distance == 0) esegui = false;
if (esegui){
stay = true;
ClearPWM();
SetOneShot();
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[15])));
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[8])));
lcd.setCursor(1, 1);
lcd.print(buff);
if (Metric){lcd.print(thread_pitch, 2); lcd.print(" mm");}
else {lcd.print(TPI); lcd.print(" TPI");}
lcd.setCursor(18, 1);
if (sviluppo_filetto) lcd.print("DX"); else lcd.print("SX");
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[12])));
lcd.setCursor(11, 3);
lcd.print(buff);
ThreadSteps = round(distance / single_step_pitch); //calculates the number of steps for the screw depending on the thread lenght
if (sviluppo_filetto == false) {
CW = !(CW); //if the threading is LEFT-HAND, temporarily invert the screw rotation
CCW = !(CCW);
}
attachInterrupt(0, InterruptEncoderFilettatura, CHANGE);
attachInterrupt(1, InterruptEncoderFilettatura, CHANGE);
step_flag = false;
steps = 0;
pointer = 0;
absolute_steps = 0;
StepperMoveToPosition(thread_offset_steps);
absolute_steps = ThreadSteps+thread_offset_steps;
absolute_encoder_steps = one_turn_mandrel_steps/2;
while (stay)
{
lcd.setCursor(0, 3);
lcd.print(" ");
pass_done = false;
while (((absolute_encoder_steps % one_turn_mandrel_steps) != 0) && (stay)) //wait here until the mandrel catch the sync with the screw
{
if (digitalRead(ESC) == PRESSED) stay = false;
}
steps = 0;
while (pass_done == false)
{
while (step_flag == false)
{
if (digitalRead(ESC) == PRESSED) {
step_flag = true;
stay = false;
pass_done = true;
}
}
step_flag = false;
if (steps == -1)
{
digitalWrite (DIR, CCW);
absolute_steps++;
pointer--;
if (pointer == -1) pointer = numero_passi - 1;
steps = sequenza[pointer] - 1;
FireStep();
}
if (steps == sequenza[pointer])
{
digitalWrite (DIR, CW);
absolute_steps--;
pointer++;
if (pointer == numero_passi) pointer = 0;
steps = 0;
FireStep();
}
if (digitalRead(ESC) == PRESSED) {
stay = false;
pass_done = true;
}
if (absolute_steps == 0) {
pass_done = true;
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[10])));
lcd.setCursor(0, 3);
lcd.print(buff);
while ((digitalRead(SEL)!= PRESSED) || (digitalRead(RESET) != PRESSED)) delayMicroseconds(1); // waits here at the end of the pass to press
StepperMoveToPosition(ThreadSteps + thread_offset_steps); // SEL+RESET to return the carriage back
// this allow to move the cross-slide back before coming back
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[11])));
lcd.setCursor(0, 3);
lcd.print(buff);
while ((digitalRead(SEL)!= PRESSED) && (digitalRead(ESC) != PRESSED)) delayMicroseconds(1); //waits here to restart - time to reposition the cross-slide
if (digitalRead(ESC) == PRESSED) stay = false;
}
}
}
detachInterrupt(0);
detachInterrupt(1);
ClearPWM();
if (sviluppo_filetto == false) {
CW = !(CW); //restore the original CW and CCW directions
CCW = !(CCW);
}
}
delay(300);
}
void Filettatura()
{
boolean resta = true;
byte Opzione;
CreaSequenza(thread_pitch); //creates the sequence array for threading
while (resta)
{
Opzione = FilettaturaMenu();
switch (Opzione) {
case 0: ImpostaPassoMetrico(); break;
case 1: ImpostaPassoPollici(); break;
case 2: Sviluppo(); break;
case 3: FilettaturaToPosition(); break;
case 4: VincoloMeccanico(); break;
case 5: resta = false; break;
}
}
}
void VincoloMeccanico() //this function simply links the screw to the mandrel with a pseudo-mechanical link, emulating the
//same situation you have on a lathe with gears or a NORTON gearbox
{
boolean stay = true; //used to stay or go out from while cycles
ClearPWM();
SetOneShot();
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[15])));
lcd.setCursor(0, 0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[7])));
lcd.setCursor(0, 1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[8])));
lcd.setCursor(1, 2);
lcd.print(buff);
if (Metric){lcd.print(thread_pitch, 2); lcd.print(" mm");}
else {lcd.print(TPI); lcd.print(" TPI");}
strcpy_P(buff, (char*)pgm_read_word(&(MenuFilettatura[12])));
lcd.setCursor(0, 3);
lcd.print(buff);
lcd.setCursor(18, 2);
if (sviluppo_filetto) lcd.print("DX"); else lcd.print("SX");
if (sviluppo_filetto == false) {
CW = !(CW); //if the threading is LEFT-HAND, temporarily invert the screw rotation
CCW = !(CCW);
}
steps = 0;
pointer = 0;
step_flag = false;
attachInterrupt(0, InterruptEncoderFilettatura, CHANGE);
attachInterrupt(1, InterruptEncoderFilettatura, CHANGE);
while (stay)
{
while (step_flag == false)
{
if (digitalRead(ESC) == PRESSED) {
step_flag = true;
stay = false;
}
}
step_flag = false;
if (steps == -1)
{
digitalWrite (DIR, CCW);
absolute_steps++;
pointer--;
if (pointer == -1) pointer = numero_passi - 1;
steps = sequenza[pointer] - 1;
FireStep();
}
if (steps == sequenza[pointer])
{
digitalWrite (DIR, CW);
absolute_steps--;
pointer++;
if (pointer == numero_passi) pointer = 0;
steps = 0;
FireStep();
}
}
detachInterrupt(0);
detachInterrupt(1);
if (sviluppo_filetto == false) {
CW = !(CW); //restore the original CW and CCW directions only if we have made a left-hand screw
CCW = !(CCW);
}
delay(300);
}
const char IMP0[] PROGMEM = "*** IMPOSTAZIONI ***";
const char IMP1[] PROGMEM = "passi enc. mandrino";
const char IMP2[] PROGMEM = "passi stepper vite";
const char IMP3[] PROGMEM = "passo vite madre";
const char IMP4[] PROGMEM = "rit. accelerazione";
const char IMP5[] PROGMEM = "rit. decelerazione";
const char IMP6[] PROGMEM = "rotazione standard";
const char IMP7[] PROGMEM = "avanzamento carro 1";
const char IMP8[] PROGMEM = "avanzamento trasv 1";
const char IMP9[] PROGMEM = "avanzamento carro 2";
const char IMP10[] PROGMEM = "avanzamento trasv 2";
const char IMP11[] PROGMEM = "avanzamento carro 3";
const char IMP12[] PROGMEM = "avanzamento trasv 3";
const char IMP13[] PROGMEM = "avanzamento carro 4";
const char IMP14[] PROGMEM = "avanzamento trasv 4";
const char IMP15[] PROGMEM = "offset filetto";
const char IMP16[] PROGMEM = "NORTON";
const char IMP17[] PROGMEM = "Vel max stepper";
const char IMP18[] PROGMEM = "ripristina valori"; //<=== FINE MENU'
const char IMP19[] PROGMEM = "SEL: OK";
const char IMP20[] PROGMEM = "ORARIA"; //VALORE PER VOCE IMP[6]
const char IMP21[] PROGMEM = "ANTI-ORARIA"; //VALORE PER VOCE IMP[6]
const char* const MenuImpostazioni[] PROGMEM = {IMP0, IMP1, IMP2, IMP3, IMP4, IMP5, IMP6, IMP7, IMP8, IMP9,
IMP10, IMP11, IMP12, IMP13, IMP14, IMP15, IMP16, IMP17, IMP18,
IMP19, IMP20, IMP21};
void Impostazioni()
{
int MenuIndex = 1;
boolean stay = true;
boolean SEL_pressed = false;
boolean ESC_pressed = false;
boolean ANALOG_X_moved = false;
boolean ANALOG_Y_moved = false;
int variation;
lcd.clear();
lcd.setCursor(0, 0);
strcpy_P(buff, (char*)pgm_read_word(&(MenuImpostazioni[0])));
lcd.print(buff);
while (stay)
{
variation = 0;
stay = true;
strcpy_P(buff, (char*)pgm_read_word(&(MenuImpostazioni[MenuIndex])));
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(buff);
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(1,2);
switch (MenuIndex) {
case 1: lcd.print(one_turn_mandrel_steps); break;
case 2: lcd.print(one_turn_screw_steps); break;
case 3: lcd.print(screw_pitch, 3); break;
case 4: lcd.print(AccelerationDelay); break;
case 5: lcd.print(DecelerationDelay); break;
case 6: if (CCW) strcpy_P(buff, (char*)pgm_read_word(&(MenuImpostazioni[20])));
else strcpy_P(buff, (char*)pgm_read_word(&(MenuImpostazioni[21])));
lcd.print(buff);
break;
case 7: lcd.print(av_carro[0]); break;
case 8: lcd.print(av_trasv[0]); break;
case 9: lcd.print(av_carro[1]); break;
case 10: lcd.print(av_trasv[1]); break;
case 11: lcd.print(av_carro[2]); break;
case 12: lcd.print(av_trasv[2]); break;
case 13: lcd.print(av_carro[3]); break;
case 14: lcd.print(av_trasv[3]); break;
case 15: lcd.print(thread_offset_steps); break;
case 16: if ( NORTON_gearbox == 4 ||( NORTON_gearbox == 0 && NORTON == false)) {lcd.print ("OFF"); NORTON = false;} else {lcd.print (NORTON_gearbox+1); NORTON = true;} break;
case 17: lcd.print(MaxStepperSpeed); break;
case 18: lcd.print("SEL:ok"); break;
}
lcd.setCursor(0, 3);
lcd.print("UP/DW - OK - ESC");
SEL_pressed = false;
ESC_pressed = false;
while (stay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED)
{
stay = false;
SEL_pressed = true;
delay(300);
}
if (digitalRead(ESC) == PRESSED)
{
stay = false;
ESC_pressed = true;
//delay(300);
lcd.setCursor(0,3);
lcd.print("..... RIAVVIO! .....");
delay(2000);
resetFunc();
}
if ((analogRead(ANALOG_X) < LOW_3) || (analogRead(ANALOG_X) > HIGH_3)) stay = false;
if ((analogRead(ANALOG_Y) < LOW_TOL) || (analogRead(ANALOG_Y) > HIGH_TOL)) stay = false;
}
stay = true;
if (analogRead(ANALOG_X) < LOW_3)
{
beepON();
MenuIndex--;
if (MenuIndex == 0) MenuIndex = 18;
delay(300);
}
if (analogRead(ANALOG_X) > HIGH_3)
{
beepON();
MenuIndex++;
if (MenuIndex == 19) MenuIndex = 1;
delay(300);
}
if (SEL_pressed)
{
if (MenuIndex == 18)
{
lcd.setCursor(0,2);
lcd.print("CONFERMA RIPRISTINO?");
lcd.setCursor(0,3);
lcd.print ("SEL:ok - ESC:annulla");
while (stay)
{
if (digitalRead(ESC) == PRESSED) stay = false;
delay(300);
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED)
{
lcd.setCursor(0,3);
lcd.print(".... RIPRISTINO ....");
FactoryDefaultEEPROM();
LoadFromEEPROM();
delay(2000);
stay = false;
lcd.setCursor(0,3);
lcd.print("..... RIAVVIO! .....");
delay(2000);
resetFunc();
}
}
stay = true;
}
else {WriteToEEPROM();LoadFromEEPROM();}
}
if (ESC_pressed) stay = false;
if ((analogRead(ANALOG_Y) < LOW_TOL) && (analogRead(ANALOG_Y) > LOW_3)) variation = -5;
if (analogRead(ANALOG_Y) <= LOW_3) variation = -50;
if ((analogRead(ANALOG_Y) > HIGH_TOL) && (analogRead(ANALOG_Y) < HIGH_3)) variation = 5;
if (analogRead(ANALOG_Y) >= HIGH_3) variation = 50;
if (variation != 0)
{
switch (MenuIndex) {
case 1: one_turn_mandrel_steps += variation; break;
case 2: one_turn_screw_steps += variation; break;
case 3: screw_pitch = (((screw_pitch * 1000) + (variation)) / 1000); break;
case 4: AccelerationDelay += (variation * 5); break;
case 5: DecelerationDelay += (variation * 5); break;
case 6: CW = !(CW); CCW = !(CCW); break;
case 7: av_carro[0] += (variation/5); break;
case 8: av_trasv[0] += (variation/5); break;
case 9: av_carro[1] += (variation/5); break;
case 10: av_trasv[1] += (variation/5); break;
case 11: av_carro[2] += (variation/5); break;
case 12: av_trasv[2] += (variation/5); break;
case 13: av_carro[3] += (variation/5); break;
case 14: av_trasv[3] += (variation/5); break;
case 15: thread_offset_steps += variation; break;
case 16: NORTON_gearbox += (variation/50);
if (NORTON_gearbox > 200 ) NORTON_gearbox = 0;
if (NORTON_gearbox > 4) NORTON_gearbox = 4;
break;
case 17: MaxStepperSpeed += variation;
}
}
delay(200);
}
}
const char MP0[] PROGMEM = "**MENU PRINCIPALE**";
const char MP1[] PROGMEM = "Avanzamento";
const char MP2[] PROGMEM = "Filettatura";
const char MP3[] PROGMEM = "Movimento Libero";
const char MP4[] PROGMEM = "Velocita' mandrino";
const char MP5[] PROGMEM = "Posizione angolare";
const char* const MenuPrincipale[] PROGMEM = {MP0, MP1, MP2, MP3, MP4, MP5};
char Principale()
{
static char current;
char previous = 5;
char next = 2;
boolean stay = true;
boolean innerstay = true;
lcd.clear();
lcd.setCursor(0,2);
lcd.print(">");
lcd.setCursor(19,2);
lcd.print("<");
strcpy_P(buff, (char*)pgm_read_word(&(MenuPrincipale[0])));
lcd.setCursor(0,0);
lcd.print(buff);
while (stay)
{
switch (current) {
case 0: current = 5; break;
case 6: current = 1; break;
}
previous = current - 1;
next = current +1;
if (previous == 0)
previous = 5;
if (next == 6)
next = 1;
strcpy_P(buff, (char*)pgm_read_word(&(MenuPrincipale[previous])));
lcd.setCursor(1,1);
lcd.print(" ");
lcd.setCursor(1,1);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuPrincipale[current])));
lcd.setCursor(1,2);
lcd.print(" ");
lcd.setCursor(1,2);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuPrincipale[next])));
lcd.setCursor(1,3);
lcd.print(" ");
lcd.setCursor(1,3);
lcd.print(buff);
innerstay = true;
while (innerstay)
{
if (digitalRead(SEL) == PRESSED || digitalRead(SEL2) == PRESSED) { stay = false; innerstay = false; delay(300); }
if (analogRead(ANALOG_Y) < LOW_1) { current -= 1; innerstay = false; beepON(); }
if (analogRead(ANALOG_Y) > HIGH_1) { current += 1; innerstay = false; beepON(); }
}
delay (300);
}
return current;
}
const char ML0[] PROGMEM = "* MOVIMENTO LIBERO *";
const char ML1[] PROGMEM = "<< - < ------ > - >>";
const char ML2[] PROGMEM = "posizione";
const char ML3[] PROGMEM = "RESET ESC";
const char* const MenuMovimentoLibero[] PROGMEM = {ML0, ML1, ML2, ML3};
void MovimentoLibero() //moves the stepper motor freely using the joystick. It returns the carriage position calculated on the actual NORTON gearbox ratio
{
boolean stay = true;
unsigned long stepperSpeed;
float Posizione = 0.00;
unsigned int joy;
int NORTON_feed;
ClearPWM();
Speed = 0;
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuMovimentoLibero[0])));
lcd.setCursor(0,0);
lcd.print (buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuMovimentoLibero[1])));
lcd.setCursor(0,1);
lcd.print (buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuMovimentoLibero[2])));
lcd.setCursor(0,2);
lcd.print (buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuMovimentoLibero[3])));
lcd.setCursor(0,3);
lcd.print (buff);
SetPWM();
delay(200);
if (CarroTrasv) NORTON_feed = av_carro[NORTON_gearbox]; else NORTON_feed = av_trasv[NORTON_gearbox];
while (stay)
{
joy = analogRead(ANALOG_X);
if (joy >= HIGH_TOL) { stepperSpeed = joy - HIGH_TOL; Direction = CCW;}
if (joy <= LOW_TOL) { stepperSpeed = LOW_TOL - joy; Direction = CW;}
if ((joy >= LOW_TOL) && (joy <=HIGH_TOL)) stepperSpeed = 0;
if (digitalRead(DIR) != Direction) {if (Speed == 0) digitalWrite(DIR, Direction); delayMicroseconds(10);}
if (digitalRead(ESC) == PRESSED) stay = false;
if (digitalRead(DIR) == Direction) StepperRunToSpeed(stepperSpeed); else StepperRunToSpeed(0);
if (digitalRead(RESET) == PRESSED) absolute_steps = 0;
delay (10);
Posizione = ((float(absolute_steps)/one_turn_screw_steps)*float(NORTON_feed))/100;
lcd.setCursor(10,2);
lcd.print(Posizione,2);
lcd.print(" mm ");
}
ClearPWM();
}
const char PA0[] PROGMEM = "POSIZIONE ANGOLARE";
const char PA1[] PROGMEM = "gradi:";
const char PA2[] PROGMEM = "RESET ESC";
const char* const MenuPosizioneAngolare[] PROGMEM = {PA0, PA1, PA2};
void InterruptEncoder() //this is the Interrupt Service Routine which manage the encoder inputs
{
static byte prev_encoder; // this variable stores the value of the input port register of the previous encoder state
byte port; // this variable stores the value of the input port register of the current encoder state
port = PIND & B00001100; //reads input pins 2 and 3 of the Arduino uno PORTD D and puts the binary image on the "port" byte variable
port |= prev_encoder; //makes the logic "OR" between the current "port" byte and the previous
steps = steps + encoder[port]; //increment/decrement the encoder couter according to the previous and current encoder values
port >>= 2; //shift sthe port image by 2 bits on right
prev_encoder = port; //assign the current port image to the "prev_encoder" byte by keeping just the bits n. 2 and 3
}
void PosizioneAngolare() // this function displays the angular position of the mandrel
{
float Degrees = 0.00; //variable used to store degrees
float old_Degrees =0.01; //variable to check if the position is changed. Set different from Degrees to print LCD output on the first cycle
const float AngularRatio = 360/float(one_turn_mandrel_steps); //calculates the degrees per single mandrel encoder step
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuPosizioneAngolare[0])));
lcd.setCursor(1,0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuPosizioneAngolare[1])));
lcd.setCursor(1,2);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuPosizioneAngolare[2])));
lcd.setCursor(0,3);
lcd.print(buff);
lcd.setCursor(8,2);
attachInterrupt(0, InterruptEncoder, CHANGE);
attachInterrupt(1, InterruptEncoder, CHANGE);
while (digitalRead(ESC) != PRESSED )
{
if (digitalRead(RESET) == PRESSED)
steps = 0;
Degrees = ((float)((abs(steps)%one_turn_mandrel_steps))*AngularRatio);
if (steps < 0) Degrees = 360 - Degrees;
if (Degrees == 360) Degrees = 0;
if (Degrees != old_Degrees)
{
lcd.setCursor(8,2);
lcd.print(Degrees);
lcd.print(" ");
old_Degrees = Degrees;
}
}
detachInterrupt(0);
detachInterrupt(1);
}
const char VM0[] PROGMEM = " VELOCITA' MANDRINO";
const char VM1[] PROGMEM = "giri al min:";
const char* const MenuVelocitaMandrino[] PROGMEM = {VM0, VM1};
void InterruptEncoderSpeed() //this is the Interrupt Service Routine which manage the encoder inputs while feeding (250 step/turn)
//activate this interrupt only with RISING on Encoder A channel (INT0)
{
absolute_encoder_steps ++;
}
void MandrelSpeed()
{
boolean stay = true; //boolean to determine if stay or not in the reading cycle
unsigned int MandrelSpeed = 0; //speed of the mandrel calculated
unsigned long prev_millis = 0; //used to store the millis alue for the speed count routine
const unsigned int interval = 100; //interval in milliseconds to measure the speed
const float speed_multiplier = ((1000/(float)interval)/((float)one_turn_mandrel_steps/4))*60; //this is the multiplier used to calculate the speed
// this is the multiplier used by to calculate the speed
// one_turn_mandrel_steps is the total number of encoder steps per every single mandrel turn
// the interrupt used in this routine is triggered only on one edge of one channel of the encoder, therefore it taked only 1 step every 4.
// 1000/(float)interval: 1000msec (1sec) divided by the interval gives the ratio of the interval with respect to 1 second
// (float)one_turn_mandrel_steps/4): the number of steps in one turn are divided by 4 (due to interrupt explained above)
// (1000/(float)interval)/((float)one_turn_mandrel_steps/4): this therefore gives the number of turn per seconds, which are then multiplied by 60 to get the speed in RPM
lcd.clear();
strcpy_P(buff, (char*)pgm_read_word(&(MenuVelocitaMandrino[0])));
lcd.setCursor(0,0);
lcd.print(buff);
strcpy_P(buff, (char*)pgm_read_word(&(MenuVelocitaMandrino[1])));
lcd.setCursor(0,2);
lcd.print(buff);
absolute_encoder_steps = 0;
prev_millis = millis();
attachInterrupt(0, InterruptEncoderSpeed, RISING);
while (stay)
{
if (digitalRead(ESC) == PRESSED || digitalRead(SEL) == PRESSED)
{
stay = false;
delay(300);
}
if (millis()-prev_millis >= interval)
{
detachInterrupt(0);
MandrelSpeed = (unsigned int)((float)absolute_encoder_steps*speed_multiplier); //calculates the speed ("/3" perchè stranamente la velocità veniva moltiplicata x 3 !!)
// CALCULATION OF SPEED
// this routine calculates how many steps are done by the mandrel during "inteval" (in milliseconds)
// the number of steps are multiplied by the "speed_multiplier" which is calculated above in the constant definition
// the result is the exact speed in round per minute
lcd.setCursor (13,2);
lcd.print(MandrelSpeed);
lcd.print(" ");
absolute_encoder_steps = 0;
prev_millis = millis();
attachInterrupt(0, InterruptEncoderSpeed, RISING);
}
}
detachInterrupt(0);
}