Building a coil winder [Part 4] - Updated feeder mechanism
In this video I talk about the most recent updates and modifications of this device. I replaced the feeder mechanism with a prebuilt linear actuator, I built a new feeder and I wrote a few new lines in the code. The feeder now work more or less well, I just need to make a few little adjustments to make it perfect. In order to understand the whole source code, especially how the display, the rotary encoder and the buttons are handled, please refer to my other video where I built a control panel for stepper motors. I basically recycled that code and added some extra lines that take care of the winding.
Control Panel Schematics
Arduino/STM32 source code
//Last update: 2021-07-30 //The LCD-related code was based on the following library: //https://github.com/cbm80amiga/N5110_GUI_encoder_demo_STM32 //Defining the pins for the Nokia 5110 LCD #define N5110_RST PA0 #define N5110_CS PA4 #define N5110_DC PA1 #define N5110_BACKLIGHT PA2 //Nokia pins: //RST: PA0 //CS: PA4 //DC: PA1 //BL: PA2 (optional) //DIN: PA7 //CLK: PA5 #include "math.h" #include "N5110_SPI.h" #if USESPI==1 #include <SPI.h> #endif N5110_SPI lcd(N5110_RST, N5110_CS, N5110_DC); // RST,CS,DC #include "c64enh_font.h" //smaller font #include "term9x14_font.h" //larger font //make sure that the font files are in the same folder as the project file! //AccelStepper #include <AccelStepper.h> AccelStepper feederStepper(1, PA8, PA9); // steps 9; direction 8 - TB6600 AccelStepper mainshaftStepper(1, PA10, PA11); // steps 11; Direction 10 - DRV8825 //Defining pins const int RotaryCLK = PB9; //CLK pin on the rotary encoder const int RotaryDT = PB8; //DT pin on the rotary encoder const int RotarySW = PB7; //SW pin on the rotary encoder (Button function) const int UpButton = PB6; //Up button which is also a start button const int DownButton = PB5; //Down button which is also a stop button const int enableMainshaft = PB10; //enable pin for the main shaft (GND = enabled, VCC = disabled) const int enableFeederShaft = PB11; //enable pin for the weaver shaft (GND = enabled, VCC = disabled) //Statuses of the DT and CLK pins on the encoder int CLKNow; int CLKPrevious; int DTNow; int DTPrevious; //Timing related variables, act mainly as debouncing float RotaryTime1; //Rotary encoder timing float RotaryTime2; unsigned long previousInterrupt; //Button timing //Menu-related variables //Values int RotaryButtonValue; //Pressed or not pressed rotary switch volatile int menuCounter = 0; //this is for counting the main menu (menu item number) // volatile int stepperMaxSpeed = 1600; //for setMaxSpeed - steps volatile int stepperAcceleration = 800; //for setAcceleration volatile float feederSpeed = 0.0; //speed of feeder in turns per second. It will be recalculated before winding volatile int numberOfTurns = 160; //number of turns of the coil - must be an integer float numberOfTurnsDone = 0; //#of turns done during winding volatile float wireDiameter = 0.35; //diameter of the wire to be wound volatile int turnsPerLayer; //Number of turns within a layer - must be an integer volatile float stepsPerLayer_feeder; //#of steps per layer for the feeder int layersToDo; //Number that stores the layers to do int numberOfLayers; //number of layers - must be an integer volatile float coilPitch = 1.0; //distance between two wires in the winding (distance between centers) volatile float bobbinWidth = 14.0; //width of the bobbin (or the coil) - unit is in mm volatile float bobbinDiameter = 20.0; // diameter of the bobbin (or the coil) - unit is in mm float totalWireLength = 0; //Total used wire length float mainShaftTotalSteps = 0; //Number of TOTAL steps for the main shaft volatile float mainShaftManualSteps = 0; float feederShaftTotalSteps = 0; //number of TOTAL steps for the secondary task volatile float feederShaftManualSteps = 0; float stepperButtonSteps = 0; // This keeps track of the steps done with the buttons int MainMicroStepping = 1600; //Value of microstepping. This amount of steps will result in 1 turn of the shaft int feederMicroStepping = 1600; //Value of microstepping. This amount of steps will result in 1 turn of the shaft //booleans bool stepperMaxSpeed_Selected = false; //for setMaxSpeed bool stepperAcceleration_Selected = false; //for setSpeed bool numberOfTurns_Selected = false; //for setAcceleration bool wireDiameter_Selected = false; //for the wire diameter bool coilPitch_Selected = false; //Coil pitch menu bool bobbinWidth_Selected = false; //0.1 mm precision bool bobbinDiameter_Selected = false; //0.1 mm precision bool mainShaftStepper_Selected = false; //precisely step the main shaft with buttons bool feederShaftStepper_Selected = false; //precisely step the weaver shaft with buttons bool winding_Selected = false; //Step size menu bool menuChanged = false; //Switched between menus bool valueChanged = false; //changed the value of a menu item bool updateValueSelection = false; //if we want to change the value of a variable, it becomes true bool windingIsRunning = false; char buf[25]; //buffer for printing on the LCD (strings) char str_temp[6]; //for printing floats int directionMultiplier = 1; //variable used to flip the direction of the feeder float mainStepperTimer = 0; //for updating the display void setup() { //Definition of the pins //Rotary encoder pinMode(PB9, INPUT); //CLK pinMode(PB8, INPUT); //DT pinMode(PB7, INPUT); //SW //Buttons pinMode(PB6, INPUT); //UP pinMode(PB5, INPUT); //DOWN //Enable-Disable pin for the stepper drivers pinMode(PB10, OUTPUT); //Main shaft enable pin pinMode(PB11, OUTPUT); //Feeder shaft enable pin //Store states CLKPrevious = digitalRead(RotaryCLK); DTPrevious = digitalRead(RotaryDT); //Rotary encoder interrupts attachInterrupt(digitalPinToInterrupt(RotaryCLK), RotaryEncoder, CHANGE); //Stepper speed setup mainshaftStepper.setMaxSpeed(stepperMaxSpeed); //SPEED = Steps / second mainshaftStepper.setAcceleration(stepperAcceleration); //ACCELERATION = Steps /(second)^2 feederStepper.setMaxSpeed(stepperMaxSpeed); feederStepper.setAcceleration(stepperAcceleration); digitalWrite(PB10, LOW); //disable main shaft power digitalWrite(PB11, LOW); //disable weaver shaft power lcd.init(); //initialize LCD lcd.clrScr(); //clear the whole display printLCD(); //print the welcome message MainMicroStepping = 1600; feederMicroStepping = 1600; //SFU1605 moves 5 mm for 1 turn -> 1 mm = 320 steps Serial.begin(115200); //All serial communication can be turned off if it is not needed } void loop() { if (menuChanged == true) { updateMenuPosition(); } if (updateValueSelection == true) { updateSelection(); updateValueSelection = false; //next loop() iteration will not enter } if (valueChanged == true) { updateValue(); valueChanged = false; } //Looping buttons CheckRotaryButton(); UpButtonPressed(); DownButtonPressed(); checkSteps(); //check the number of steps done //We don't need accelerations if we use slow speeds anyway, so I used the "acceleration-less" functions: mainshaftStepper.runSpeedToPosition(); //If a step is due, this function will do a step in each loop() iteration feederStepper.runSpeedToPosition(); //If a step is due, this function will do a step in each loop() iteration if (windingIsRunning == true) { //calculate the number of turns and refresh the display during winding every 1 sec //Printing might cause little "click" sounds when the motor is turning --> this part perhaps could have been done in a more elegant way if (millis() - mainStepperTimer > 1000) { numberOfTurnsDone = (mainShaftTotalSteps - mainshaftStepper.distanceToGo()) / (float)MainMicroStepping; //Example: (16000 - 4000) / 400 = 12000 / 400 = 30 updateValue(); mainStepperTimer = millis(); } } } void RotaryEncoder() { //Stepper speed - defined in steps/sec if (stepperMaxSpeed_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { stepperMaxSpeed++ ; //1 step size increment } else { if (stepperMaxSpeed == 160) //160, depends on the microstepping! { // Don't decrease further, it should be at least 160 // 1600/10 = 160 -> 0.1 turns/sec is the minimum speed } else { stepperMaxSpeed--; //1 step size decrement } } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state } //---------------------------------------------------------------------------- //Stepper acceleration else if (stepperAcceleration_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { stepperAcceleration++; //1 step size increment } else { stepperAcceleration--; //1 step size decrement } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state } //---------------------------------------------------------------------------- //Number of turns else if (numberOfTurns_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { numberOfTurns++; //1 step size increment } else { if (numberOfTurns == 1) //when it is = 1 { // Don't decrease further, it should be at least 1 } else { numberOfTurns--; //1 step size decrement } } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state } //---------------------------------------------------------------------------- //Wire diameter else if (wireDiameter_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { wireDiameter = wireDiameter + 0.01; //0.01 step size increment } else { if (wireDiameter == 0.01) //when it is = 0.01 { // Don't decrease further, it should be at least 0.01 } else { wireDiameter = wireDiameter - 0.01; //0.01 step size decrement } } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state } //---------------------------------------------------------------------------- //Coil pitch (the pitch function is not yet implemented! - 2021-07-30) else if (coilPitch_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { coilPitch = coilPitch + wireDiameter; //wire diameter step size increment } else { if (coilPitch == wireDiameter) //when it is = wire diameter { // Don't decrease further, it should be at least 1 diameter } else { coilPitch = coilPitch - wireDiameter; //wire diameter step size increment } } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state } //---------------------------------------------------------------------------- //Bobbin width else if (bobbinWidth_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { bobbinWidth = bobbinWidth + 0.1; //0.1 mm step size increment } else { if (bobbinWidth == 0.1) //when it is = 0.1 { // Don't decrease further, it should be at least 0.1 } else { bobbinWidth = bobbinWidth - 0.1; //0.01 step size decrement } } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state } //---------------------------------------------------------------------------- //Bobbin diameter - technically, the inner diameter of the coil else if (bobbinDiameter_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { bobbinDiameter = bobbinDiameter + 0.1; //0.1 mm step size increment } else { if (bobbinDiameter == 0.1) //when it is = 0.1 { // Don't decrease further, it should be at least 0.1 } else { bobbinDiameter = bobbinDiameter - 0.1; //0.01 step size decrement } } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state } //---------------------------------------------------------------------------- //Main shaft manual stepping else if (mainShaftStepper_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { mainShaftManualSteps = mainshaftStepper.currentPosition() + 160; //160 step size increment relative to the current position (depends on the microstepping) //160 steps is 0.1 turn, decrease the number if the movement is too much } else { mainShaftManualSteps = mainshaftStepper.currentPosition() - 160; //160 step size decrement relative to the current position } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state mainshaftStepper.moveTo(mainShaftManualSteps); //move to because we move relatively in a sense that we add a number to the absolute position } //---------------------------------------------------------------------------- //Feeder shaft manual stepping else if (feederShaftStepper_Selected == true) { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating in A direction, so we increase if (digitalRead(RotaryDT) == CLKNow) { feederShaftManualSteps = feederStepper.currentPosition() + 160; //160 step size increment relative to the current position //320 steps is 1 mm with 1600 steps/turn microstepping ---> 160 steps is 0.5 mm } else { feederShaftManualSteps = feederStepper.currentPosition() - 160; //160 step size decrement relative to the current position } valueChanged = true; } CLKPrevious = CLKNow; // Store last CLK state feederStepper.moveTo(feederShaftManualSteps); } //---------------------------------------------------------------------------- // else if (winding_Selected == true) { //do nothing } else //MENU COUNTER---------------------------------------------------------------------------- { CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin // If last and current state of CLK are different, then a pulse occurred if (CLKNow != CLKPrevious && CLKNow == 0) { // If the DT state is different than the CLK state then // the encoder is rotating CCW so increase if (digitalRead(RotaryDT) == CLKNow) { if (menuCounter < 9) //10 menu items (0, 1, 2....9) { menuCounter++; } else { menuCounter = 0; //0 comes after 8, so we move in a "ring" } menuChanged = true; } else { // Encoder is rotating CW so decrease if (menuCounter > 0) { menuCounter--; } else { menuCounter = 9; //9 comes after 0 when we decrease the numbers } menuChanged = true; } } CLKPrevious = CLKNow; // Store last CLK state } } void printLCD() //Prints a "welcome screen" { lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, "Digital"); lcd.printStr(ALIGN_CENTER, 1, "Solenoid"); lcd.printStr(ALIGN_CENTER, 3, "Coil"); lcd.printStr(ALIGN_CENTER, 4, "Winder"); lcd.printStr(ALIGN_CENTER, 5, "C. Scientist"); } void CheckRotaryButton() { RotaryButtonValue = digitalRead(RotarySW); //read the button state if (RotaryButtonValue == 0) //0 and 1 can differ based on the wiring { RotaryTime1 = millis(); if (RotaryTime1 - RotaryTime2 > 1000) { switch (menuCounter) { case 0: stepperMaxSpeed_Selected = !stepperMaxSpeed_Selected; //we change the status of the variable to the opposite break; // case 1: stepperAcceleration_Selected = !stepperAcceleration_Selected; break; // case 2: numberOfTurns_Selected = !numberOfTurns_Selected; break; // case 3: wireDiameter_Selected = !wireDiameter_Selected; break; // case 4: coilPitch_Selected = !coilPitch_Selected; break; // case 5: bobbinWidth_Selected = !bobbinWidth_Selected; break; // case 6: bobbinDiameter_Selected = !bobbinDiameter_Selected; break; // case 7: mainShaftStepper_Selected = !mainShaftStepper_Selected; break; // case 8: feederShaftStepper_Selected = !feederShaftStepper_Selected; break; // case 9: winding_Selected = !winding_Selected; break; } updateValueSelection = true; RotaryTime2 = millis(); } } } void updateMenuPosition() { lcd.clrScr();//delete the entire screen switch (menuCounter) //this checks the value of the counter (0, 1, 2 or 3) { //MAX SPEED case 0: lcd.setFont(c64enh); //set the font (small one) lcd.printStr(ALIGN_CENTER, 0, " "); //erase previous text lcd.printStr(ALIGN_CENTER, 0, "Speed "); //print new text lcd.setFont(Term9x14); //Set another (larger) font snprintf(buf, 8, "%d", stepperMaxSpeed); //the decimal value of the variable is in the buf lcd.printStr(15, 2, buf); //set the cursor to 2nd line 15th pixel and print the buffer break; //------------------------------- //ACCELERATION case 1: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Acceleration"); lcd.setFont(Term9x14); snprintf(buf, 8, "%d", stepperAcceleration); lcd.printStr(15, 2, buf); break; //------------------------------- //Number of turns case 2: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Turns"); lcd.setFont(Term9x14); snprintf(buf, 8, "%d", numberOfTurns); lcd.printStr(15, 2, buf); break; //------------------------------- //Wire diameter case 3: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Wire diameter"); lcd.setFont(Term9x14); dtostrf(wireDiameter, 4, 2, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(15, 2, buf); break; //------------------------------- //Coil pitch case 4: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Pitch"); lcd.setFont(Term9x14); dtostrf(coilPitch, 4, 2, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(15, 2, buf); break; //------------------------------- //Bobbin width case 5: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Bobbin width"); lcd.setFont(Term9x14); dtostrf(bobbinWidth, 4, 2, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(15, 2, buf); break; //------------------------------- //Bobbin diameter case 6: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Bobbin dia."); lcd.setFont(Term9x14); dtostrf(bobbinDiameter, 4, 2, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(15, 2, buf); break; //------------------------------- //MOVEMENT OF THE MAIN SHAFT (Button or rotary) case 7: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Main shaft"); lcd.setFont(Term9x14); snprintf(buf, 8, "%d", mainshaftStepper.currentPosition()); //the decimal value of the variable is in the buf lcd.printStr(15, 2, buf); break; //------------------------------- //MOVEMENT OF THE FEEDER SHAFT (Button or rotary) case 8: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Feeder shaft"); lcd.setFont(Term9x14); snprintf(buf, 8, "%d", feederStepper.currentPosition()); lcd.printStr(15, 2, buf); break; //------------------------------- //START-STOP auto winding case 9: lcd.setFont(c64enh); lcd.printStr(ALIGN_CENTER, 0, " "); lcd.printStr(ALIGN_CENTER, 0, "Auto winding"); lcd.printStr(12, 1, "Layers left:"); lcd.printStr(12, 2, "0"); lcd.printStr(12, 3, "Turns done: "); lcd.printStr(12, 4, "0"); lcd.printStr(0, 5, "Length: "); break; } menuChanged = false; //next loop() iteration will not enter again } void updateValue() { switch (menuCounter) //this checks the value of the counter (0, 1, 2 or 3) { //Max speed menu case 0: lcd.setFont(Term9x14); lcd.printStr(15, 2, " "); snprintf(buf, 8, "%d", stepperMaxSpeed); lcd.printStr(15, 2, buf); break; //------------------------------- //Acceleration menu - In this version, the acceleration does not play any role case 1: mainshaftStepper.setAcceleration(stepperAcceleration); //both shafts accelerate in the same way feederStepper.setAcceleration(stepperAcceleration); //it has to be tested lcd.setFont(Term9x14); lcd.printStr(15, 2, " "); snprintf(buf, 8, "%d", stepperAcceleration); lcd.printStr(15, 2, buf); break; //------------------------------- //Number of turns case 2: lcd.setFont(Term9x14); lcd.printStr(15, 2, " "); snprintf(buf, 8, "%d", numberOfTurns); lcd.printStr(15, 2, buf); break; //------------------------------- //Wire diameter case 3: lcd.setFont(Term9x14); //Command values lcd.printStr(15, 2, " "); dtostrf(wireDiameter, 4, 2, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(15, 2, buf); break; //------------------------------- //Coil pitch case 4: lcd.setFont(Term9x14); lcd.printStr(15, 2, " "); dtostrf(coilPitch, 4, 2, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(15, 2, buf); break; //------------------------------- //Bobbin width case 5: lcd.setFont(Term9x14); lcd.printStr(15, 2, " "); dtostrf(bobbinWidth, 4, 2, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(15, 2, buf); break; //------------------------------- //Bobbin diameter case 6: lcd.setFont(Term9x14); lcd.printStr(15, 2, " "); dtostrf(bobbinDiameter, 4, 2, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(15, 2, buf); break; //------------------------------- case 7: lcd.setFont(Term9x14); lcd.printStr(15, 2, " "); snprintf(buf, 8, "%d", mainshaftStepper.currentPosition()); lcd.printStr(15, 2, buf); break; //------------------------------- case 8: lcd.setFont(Term9x14); lcd.printStr(15, 2, " "); snprintf(buf, 8, "%d", feederStepper.currentPosition()); lcd.printStr(15, 2, buf); break; //------------------------------- //Auto Winding case 9: lcd.setFont(c64enh); //line 2 - Layers left lcd.printStr(12, 2, " "); snprintf(buf, 8, "%d", layersToDo); lcd.printStr(12, 2, buf); //line 4 - Turns left lcd.printStr(12, 4, " "); dtostrf(numberOfTurnsDone, 4, 1, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(12, 4, buf); //line 5 - consumed wire length lcd.printStr(45, 5, " "); dtostrf(totalWireLength, 4, 1, str_temp); snprintf(buf, 8, "%s", str_temp); lcd.printStr(45, 5, buf); break; } valueChanged = false; //next loop() iteration will not enter again } void updateSelection() { lcd.setFont(Term9x14); //Large font size for the cursor (same as of the values) if (stepperMaxSpeed_Selected == true) { snprintf(buf, 8, "%s", ">"); //print the ">" as the cursor, the ">" indicates the selection/activation lcd.printStr(0, 2, buf); //put it on the display: 2nd line 0 pixel return; } else if (stepperAcceleration_Selected == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else if (numberOfTurns_Selected == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else if (wireDiameter_Selected == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else if (coilPitch_Selected == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else if (bobbinWidth_Selected == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else if (bobbinDiameter == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else if (mainShaftStepper_Selected == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else if (feederShaftStepper_Selected == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else if (winding_Selected == true) { snprintf(buf, 8, "%s", ">"); lcd.printStr(0, 2, buf); return; } else //If a menu item is unselected, this else branch is performed and the ">" symbol is erased { snprintf(buf, 8, "%s", " "); lcd.printStr(0, 2, buf); return; } } void UpButtonPressed() { if (digitalRead(UpButton) == 0) //1 or 0, depends on your wiring also! { if (millis() - previousInterrupt > 300) //debounce, sort of. If another click comes within 300 ms, we don't care about it { //Increase values - buttons increase/decrease the input values with larger steps than the rotary encoder if (stepperMaxSpeed_Selected == true) { stepperMaxSpeed = stepperMaxSpeed + 40; } else if (stepperAcceleration_Selected == true) { stepperAcceleration = stepperAcceleration + 40; } else if (numberOfTurns_Selected == true) { numberOfTurns = numberOfTurns + 10; } else if (bobbinWidth_Selected == true) { bobbinWidth = bobbinWidth + 1.0; } else if (bobbinDiameter_Selected == true) { bobbinDiameter = bobbinDiameter + 1; } else if (winding_Selected == true) { //reset positions if there was a previous movement - this will ease things for the moveTo() function (and for us) mainshaftStepper.setCurrentPosition(0); feederStepper.setCurrentPosition(0); //Recalculate the speeds for the feeder calculateSpeeds(); //Define the amount of steps to move for both motors mainshaftStepper.moveTo(mainShaftTotalSteps - (2 * MainMicroStepping)); // instruct the main shaft to move the total steps (-2 turns) feederStepper.moveTo(stepsPerLayer_feeder - (2 * wireDiameter * (feederMicroStepping / 5.0)) ); // instruct the feeder to move 1 bobbin width distance //We also subtract 2 turns equivalent of distance from the total steps because those are done manually when we start the coil //Set speeds feederStepper.setMaxSpeed(feederSpeed); feederStepper.setSpeed(feederSpeed); // mainshaftStepper.setMaxSpeed(stepperMaxSpeed); mainshaftStepper.setSpeed(stepperMaxSpeed); windingIsRunning = true; } //------------------------------------------------------------- //Button movements - you can place them outside of the if(), it will be faster else if (mainShaftStepper_Selected == true) { stepperButtonSteps = mainshaftStepper.currentPosition() + 160; //increase the target position by 1 stepsize: 0.1 turn mainshaftStepper.setMaxSpeed(400); mainshaftStepper.moveTo(stepperButtonSteps); //move the main shaft } else if (feederShaftStepper_Selected == true) { stepperButtonSteps = feederStepper.currentPosition() + 32; //increase the target position by 1 stepsize: 0.1 mm feederStepper.setMaxSpeed(400); feederStepper.moveTo(stepperButtonSteps); //move the secondary shaft } valueChanged = true; //display has to be updated previousInterrupt = millis(); //update the value so the counting from 300 ms is started over from 0 } } } void DownButtonPressed() { if (digitalRead(DownButton) == 0) //1 or 0, depends on your wiring also! { if (millis() - previousInterrupt > 300) { //Decrease values if (stepperMaxSpeed_Selected == true) //add 10 to the number { if (stepperMaxSpeed > 40) { stepperMaxSpeed = stepperMaxSpeed - 40; } else { //do nothing } } else if (stepperAcceleration_Selected == true) { if (stepperAcceleration > 40) { stepperAcceleration = stepperAcceleration - 40; } else { //do nothing } } else if (numberOfTurns_Selected == true) { if (numberOfTurns > 10) { numberOfTurns = numberOfTurns - 10; } else { //do nothing } } else if (bobbinWidth_Selected == true) { if (bobbinWidth > 1.0) { bobbinWidth = bobbinWidth - 1.0; } else { //do nothing } } else if (bobbinDiameter_Selected == true) { if (bobbinDiameter > 1.0) { bobbinDiameter = bobbinDiameter - 1.0; } else { //do nothing } } else if (winding_Selected == true) { windingIsRunning = false; mainshaftStepper.stop(); feederStepper.stop(); } //------------------------------------------------------------- //Button movements else if (mainShaftStepper_Selected == true) { stepperButtonSteps = mainshaftStepper.currentPosition() - 160; //increase the target position by 1 stepsize mainshaftStepper.setMaxSpeed(400); //we must set a speed because the winding restarts it mainshaftStepper.moveTo(stepperButtonSteps); //move the main shaft } else if (feederShaftStepper_Selected == true) { stepperButtonSteps = feederStepper.currentPosition() - 32; //increase the target position by 1 stepsize feederStepper.setMaxSpeed(400); feederStepper.moveTo(stepperButtonSteps); //move the secondary shaft } valueChanged = true; //display has to be updated previousInterrupt = millis(); //update the value so the counting from 300 ms is started over from 0 } } } void calculateSpeeds() { //The feeder stepper should move 1 wire diameter for each turn of the main shaft mainShaftTotalSteps = numberOfTurns * MainMicroStepping; //calculate the total amount of steps for the main shaft //Example: MSTS = 160 turns * 1600 steps/turn = 256000 steps total //Serial.print("MainshaftTotalSteps: "); //Serial.println(mainShaftTotalSteps); //OK turnsPerLayer = bobbinWidth / wireDiameter; //How many wires can be fitted next to each other within the bobbin width //Example: TPL = 14 mm / 0.35 mm = 40 turns per layer //Serial.print("TurnsPerLayer: "); //Serial.println(turnsPerLayer); //OK numberOfLayers = numberOfTurns / turnsPerLayer; //total number of turns / turns per layer //Example: NOL = 160 turns / 40 turns per layer = 4 layers //Notice: Always round the result UP (ceiling) //Serial.print("NumberOfLayers: "); //Serial.println(numberOfLayers); //OK layersToDo = numberOfLayers; //layersToDo stores a "dynamic" value //Serial.print("Layers to do: "); //Serial.println(layersToDo); //OK feederShaftTotalSteps = numberOfLayers * bobbinWidth * (feederMicroStepping / 5); // division by 5 = this much step results in 1 mm movement of the feeder (ballscrew) //Example2: FSTL = 4 * 14 mm * 1600 steps per turn / 5 = 17920 //Serial.print("feederShaftTotalSteps: "); //Serial.println(feederShaftTotalSteps); //OK stepsPerLayer_feeder = feederShaftTotalSteps / (numberOfLayers); //Serial.print("feederStepsPerLayer: "); //Serial.println(stepsPerLayer_feeder); //Example: SPL_f = 17920 steps / 4 layers = 4480 steps/layer //OK feederSpeed = ((float)stepperMaxSpeed / (float)MainMicroStepping) * (float)feederMicroStepping * wireDiameter / 5.0 ; //division by 5 is related to the pitch of the ballscrew //Example: FS = (1600/1600) * 1600 * 0.35 mm / 5 = 1 * 320 * 0.35 mm = 112 steps / second //Serial.print("feederSpeed: "); //Serial.println(feederSpeed); //OK //Serial.println("Calculation finished."); //Serial.println("---------------------"); directionMultiplier = 1; //reset direction multiplier to 1. totalWireLength = 0; //reset wire length in case there was a winding without device reset } void checkSteps() { if (windingIsRunning == true) { if (feederStepper.distanceToGo() == 0) //if feeder finished a layer { //Serial.print("Main stepper position: "); //Serial.println(mainshaftStepper.currentPosition()); if (layersToDo != 0) //if it was not the last layer { //Serial.print("Layers to do: "); //Serial.println(layersToDo); winding(); updateValue(); //at each call of the winding() function we update the layers and the turns } } //The main issue is to sync the two motors together so they accelerate and move in a synced fashion if (mainshaftStepper.distanceToGo() == 0) //if the main shaft finished { windingIsRunning = false; //In the very last step, we add the final length of the last layer totalWireLength += ((float)(numberOfTurns - turnsPerLayer * (numberOfLayers - 1)) * 6.2832 * ((bobbinDiameter/2) + wireDiameter / 2 + ((float)(numberOfLayers - layersToDo) * 1.732 * (wireDiameter/2) ))) / (float)1000; //sqrt(3) = 1.732 //1000 division -> mm to m conversion updateValue(); } } else { //skip everything } } void winding() { //After we switched directions, we can add the full layer's length totalWireLength += ((float)turnsPerLayer * 6.2832 * ((bobbinDiameter/2) + (wireDiameter / 2) + ( (float)(numberOfLayers - layersToDo) * 1.732 * (wireDiameter/2) ))) / (float)1000; //Serial.print("Temp length: "); //Serial.println(totalWireLength); layersToDo--; //When the code enters this part, 1 layer is already laid. So we decrease this variable and then decide what to do next. directionMultiplier = -1 * directionMultiplier; //We flip the direction each time we enter. (-1 * 1 = -1, then -1 * -1 = 1...etc.) if (layersToDo > 1) //if there are more than 1 layers have to be done, we proceed as usual { //Serial.println("Layers to do > 1"); //Serial.print("Layers to do: "); //Serial.println(layersToDo); //Serial.print("stepsPerLayer_feeder"); //Serial.println(stepsPerLayer_feeder); //Serial.print("Adjusted steps"); //Serial.println(directionMultiplier * (stepsPerLayer_feeder - 2 * wireDiameter * (feederMicroStepping / 5.0))); feederStepper.move(directionMultiplier * (stepsPerLayer_feeder - (2 * wireDiameter * (feederMicroStepping / 5.0))) ); //Subtracting 2 wire diameter equivalent of distance because we will not go to the edge of the bobbin (the 2x multiplier is somewhat experimental) feederStepper.setSpeed(feederSpeed); } else if (layersToDo == 1) //if there is only 1 layer (last one) is left, we also have to consider how much turns we need on the last layer { //Serial.println("Layers to do == 1"); feederStepper.move(directionMultiplier * (wireDiameter * (feederMicroStepping / 5.0) * (numberOfTurns - (numberOfLayers - 1) * turnsPerLayer ))); //EX: 1 * 0.35 * (1600/5) * (135 - (4-1) * 40 = 0.35 * 320 * (135-120) = 1680 steps // feederStepper.setSpeed(feederSpeed); } else //this is when layersToDo == 0. We are finished, we do not move anywhere anymore { //Serial.println("Layers to do == 0 - feeder has finished"); } }