// Wickelmaschine v4: Softanlauf eingebaut // // LCD // 1) Arduino has two pins called SDA and SCL. These are different on different arduino boards. On the UNO R3 these are // pin A4 (SDA) and A5 (SCL) for example // 2) Also please check your I2C devices address (used in the LiquidCrystal_I2C lcd() call). Mine is 0x3F or 0x27. // You can check the address with this I2C scanner: https://osoyoo.com/wp-content/uploads/samplecode/ic2_scanner.txt // LCD display driver #include #include LiquidCrystal_I2C lcd(0x20, 20, 4); // Stepper driver #include #include "BasicStepperDriver.h" #define MOTOR_STEPS 200 // 360/1,8° (see datasheet of 42HS34-1304A stepper) #define M1STEPS 16 // microstepping 1/16 via dip switches on board #define M1DIR 7 #define M1STEP 6 #define M1SLEEP 8 BasicStepperDriver coilStepper(MOTOR_STEPS, M1DIR, M1STEP, M1SLEEP); // x-axis motor #define M2STEPS 32 // microstepping 1/32 via dip switches on board #define M2DIR 4 #define M2STEP 5 #define M2SLEEP 12 BasicStepperDriver wireStepper(MOTOR_STEPS, M2DIR, M2STEP, M2SLEEP); // x-axis motor // Buttons #define BUTTON1 13 #define BUTTON2 11 #define BUTTON3 10 #define BUTTON4 9 // Photoelectric barrier #define PBARR 39 // Endstop #define ERIGHT 50 #define ELEFT 52 // Rotary encoder #define CLK 46 #define DATA 42 #define SW 44 // Rotary encoder bool encChanged = false; // true if encoder knob was used bool knobLeft = false; // Determine last knob direction static uint8_t prevNextCode = 0; static uint16_t store = 0; // Einstellungen für Windemaschine #define MAX_SLED_DISTANCE 363.0 // Fahrstrecke des Schlittens in mm (= Distanz zwischen rechter Seite des Schlittens und "grade eben" aktiviertem rechten Endstop) long maxSledSteps = 58176; // Anzahl Schritte, um 363mm (s. o.) zurückzulegen (aus der Kalibrierung ermittelt). Der Drahtmotor befindet sich im 1/16 Schrittmodus (low/low/high in den DIP-Schaltern) long sledPos = -1; // Aktuelle Schlittenposition in Schritten (-1 = undefiniert) long stepsPerTurn = 0; // Anzahl Schritte des Schlittens pro Umdrehung der Spule (entspricht dem Drahtdurchmesser in Schritten!) #define S_ROTATE_RPM 5 // Rotiergeschwindigkeit für Fkt. "Rotate only" #define S_WIND_COIL_RPM 30 // Anzahl Umdrehungen beim Wickeln // Menüs / Screens #define S_SPLASH 0 // Splash screen #define S_MAIN 1 // Hauptmenü #define S_SETUP 2 // Setup menu #define S_ROTATE 3 // Rotate only (without feeding wire) #define S_WIND 4 // Wind coil #define S_CALIBRATE 5 // Maschine kalibrieren #define S_MOVE 6 // Schlitten bewegen #define S_PARAMS 7 // Parameter (Drahtstärke etc.) einstellen #define S_HOME 8 // Auto home sled #define S_BOBBIN 9 // Bobbin wickeln für Trafo (hin- und her / Lagen übereinander) #define S_ABOUT 10 // About screen int screen = S_SPLASH; // Startmenü char *scrText[10]; // global array for menu lists int scrItem[10]; // Rotary encoder reader - stolen from https://www.best-microcontroller-projects.com/rotary-encoder.html#Taming_Noisy_Rotary_Encoders // A valid CW or CCW move returns 1, invalid returns 0. int8_t read_rotary() { static int8_t rot_enc_table[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0}; prevNextCode <<= 2; if (digitalRead(DATA)) prevNextCode |= 0x02; if (digitalRead(CLK)) prevNextCode |= 0x01; prevNextCode &= 0x0f; // If valid then store as 16 bit data. if (rot_enc_table[prevNextCode] ) { store <<= 4; store |= prevNextCode; //if (store==0xd42b) return 1; //if (store==0xe817) return -1; if ((store & 0xff) == 0x2b) return -1; if ((store & 0xff) == 0x17) return 1; } return 0; } // Schlitten in Startposition bewegen (ganz links) void homePosition() { wireStepper.enable(); while (!endstopRight()) { wireStepper.move(-M2STEPS); delay(3); } delay(500); while (endstopRight()) { wireStepper.move(M2STEPS); delay(5); } wireStepper.disable(); } // Konvertiert Schritte in ganze mm für die Drahtachse double stepsToMm(long st) { return (st / (double) maxSledSteps * MAX_SLED_DISTANCE); } // Konvertiert mm in Schritte für die Drahtachse long mmToSteps(float mm) { return (mm * maxSledSteps / MAX_SLED_DISTANCE); } void setup() { Serial.begin(9600); // for debug output (Tools -> Serial monitor) // LCD init lcd.init(); lcd.backlight(); lcd.blink(); // Stepper coilStepper.begin(); coilStepper.setMicrostep(M1STEPS); coilStepper.setEnableActiveState(LOW); coilStepper.disable(); wireStepper.begin(); coilStepper.setMicrostep(M2STEPS); wireStepper.setEnableActiveState(LOW); wireStepper.disable(); // Buttons pinMode(BUTTON1, INPUT); pinMode(BUTTON2, INPUT); pinMode(BUTTON3, INPUT); pinMode(BUTTON4, INPUT); // Photoelectric barrier pinMode(PBARR, INPUT); pinMode(PBARR, INPUT_PULLUP); // Endstops pinMode(ELEFT, INPUT); pinMode(ERIGHT, INPUT); // Rotary encoder pinMode(CLK, INPUT); pinMode(CLK, INPUT_PULLUP); pinMode(DATA, INPUT); pinMode(DATA, INPUT_PULLUP); pinMode(SW, INPUT); } // --------------- Funktionen -------------------- boolean endstopLeft() { return (digitalRead(ELEFT) == LOW); } boolean endstopRight() { return (digitalRead(ERIGHT) == LOW); } boolean button1() { // add delay(200) for debouncing if routine returns with true state! return (digitalRead(BUTTON1) == LOW); } boolean button2() { // add delay(200) for debouncing if routine returns with true state! return (digitalRead(BUTTON2) == LOW); } boolean button3() { // add delay(200) for debouncing if routine returns with true state! return (digitalRead(BUTTON3) == LOW); } boolean button4() { // add delay(200) for debouncing if routine returns with true state! return (digitalRead(BUTTON4) == LOW); } boolean barrier() { return (digitalRead(PBARR) == LOW); } // Return true if light barrier was entered bool lastBarrierState = barrier(); boolean enteredBarrier() { bool nextBarrierState = barrier(); bool state = (lastBarrierState == false && nextBarrierState == true); lastBarrierState = nextBarrierState; return state; } boolean encoderPress() { // add delay(200) for debouncing if routine returns with true state! return (digitalRead(SW) == LOW); } boolean encoderRotate() { // Check if rotary encoder knob was turned, stores left/right in global var "knobLeft" static int8_t c, val; if (val = read_rotary()) { knobLeft = true; if (val == 1) knobLeft = false; return true; } return false; } void clearLCD() { for (int i = 0; i <= 3; i++) { lcd.setCursor(0, i); lcd.print(" "); } } int menuSelector(int n) { // Überschrift clearLCD(); lcd.blink(); lcd.setCursor(0, 0); lcd.print(scrText[0]); // Menu loop int item; int start = 1; int cursor = 0; bool update = true; while (1) { if (update) { for (int i = 0; i < 3; i++) { lcd.setCursor(0, 1 + i); if (start + i <= n) lcd.print(scrText[start + i]); else lcd.print(" "); } lcd.setCursor(0, 1 + cursor); update = false; } if (encoderRotate()) { if (knobLeft) cursor--; else cursor++; // Scroll list if cursor exceeds bottom if (cursor > 2) { cursor = 2; if (start + cursor < n) start++; } // Special treatment if list is shorter than three items if (cursor >= n) cursor--; // Scroll list if cursor exceeds top if (cursor < 0) { cursor = 0; if (start > 1) start--; } update = true; } if (encoderPress()) { item = scrItem[start + cursor]; break; } // scrItem[0] always points to upper menu (aka "back button") if (button1()) { item = scrItem[0]; break; } } delay(200); return item; } void loop() { int n; Serial.print("Menu = "); Serial.println(screen); switch (screen) { // Splash screen case S_SPLASH: { Serial.println("Splash screen"); clearLCD(); lcd.noBlink(); lcd.setCursor(4, 0); lcd.print("Coil winding"); lcd.setCursor(6, 1); lcd.print("machine"); lcd.setCursor(9, 2); lcd.print("by"); lcd.setCursor(4, 3); lcd.print("TeslaUndMehr"); delay(2500); screen = S_MAIN; break; } // Main menu case S_MAIN: { Serial.println("Main menu"); scrText[0] = " -- Main menu -- "; scrItem[0] = S_MAIN; scrText[1] = "Prepare "; scrItem[1] = S_SETUP; scrText[2] = "Rotate only "; scrItem[2] = S_ROTATE; scrText[3] = "Wind coil "; scrItem[3] = S_WIND; scrText[4] = "Wind bobbin "; scrItem[4] = S_BOBBIN; scrText[5] = "About "; scrItem[5] = S_ABOUT; screen = menuSelector(5); break; } // Setup case S_SETUP: { Serial.println("Setup menu"); scrText[0] = " -- Setup -- "; scrItem[0] = S_MAIN; scrText[1] = "Set wire parameters "; scrItem[1] = S_PARAMS; scrText[2] = "Auto home "; scrItem[2] = S_HOME; scrText[3] = "Move sled "; scrItem[3] = S_MOVE; scrText[4] = "Calibrate "; scrItem[4] = S_CALIBRATE; screen = menuSelector(4); break; } // Rotate only case S_ROTATE: { Serial.println("Rotate coil"); clearLCD(); lcd.blink(); lcd.setCursor(0, 0); lcd.print(" -- Rotate coil -- "); lcd.setCursor(0, 1); lcd.print("Press [Start/Stop] "); lcd.setCursor(0, 2); lcd.print("to begin rotation "); lcd.setCursor(0, 3); lcd.print("or [<-] to return. "); boolean isRunning = false; while (!button1()) { if (button4()) { isRunning = !isRunning; if (isRunning) { coilStepper.enable(); coilStepper.setRPM(S_ROTATE_RPM); delay(200); } else { coilStepper.disable(); delay(400); } } if (isRunning) coilStepper.move(MOTOR_STEPS * M1STEPS); } coilStepper.disable(); delay(200); // debounce screen = S_MAIN; break; } // Calibrate machine case S_CALIBRATE: { Serial.println("Calibrate"); clearLCD(); lcd.noBlink(); lcd.setCursor(0, 0); lcd.print(" -- Calibrate -- "); lcd.setCursor(0, 1); lcd.print("Steps: "); lcd.setCursor(0, 2); lcd.print("Distance: "); lcd.setCursor(0, 3); lcd.print("[<-] Back [Start]"); sledPos = -1; while (!button1()) { if (button4()) { lcd.setCursor(0, 3); lcd.print(" [auto home] "); homePosition(); lcd.setCursor(0, 3); lcd.print(" [in progress] "); wireStepper.enable(); long steps = 0; int i = 48; while (!endstopLeft()) { wireStepper.move(M2STEPS); steps += M2STEPS; i++; if (i == 49) { // nur zur Show, damit die Zahlen etwas "bewegter" wirken lcd.setCursor(10, 1); lcd.print(steps); lcd.setCursor(10, 2); lcd.print(MAX_SLED_DISTANCE / maxSledSteps * steps); i = 0; } else { delay(5); } } lcd.setCursor(10, 1); lcd.print(steps); lcd.setCursor(10, 2); lcd.print(MAX_SLED_DISTANCE / maxSledSteps * steps); maxSledSteps = steps; lcd.setCursor(0, 3); lcd.print("Steps/mm: "); lcd.setCursor(10, 3); lcd.print(((float) maxSledSteps) / MAX_SLED_DISTANCE); homePosition(); sledPos = 0; break; } } screen = S_SETUP; break; } // Move sled case S_MOVE: { Serial.println("Move"); clearLCD(); lcd.setCursor(0, 0); lcd.print(" -- Move sled -- "); if (sledPos == -1) { lcd.setCursor(0, 1); lcd.print("Sled at unknown "); lcd.setCursor(0, 2); lcd.print("pos. Please auto "); lcd.setCursor(0, 3); lcd.print("home first! [<-]"); while (!button1()); delay(200); // debounce } else { int targetMm = 0; lcd.setCursor(0, 0); lcd.print(" -- Move sled -- "); lcd.setCursor(0, 1); lcd.print("Position: mm"); lcd.setCursor(0, 2); lcd.print("Target: mm"); lcd.setCursor(0, 3); lcd.print("[<-] Back [Start]"); bool update = true; while (!button1()) { if (update) { lcd.setCursor(13, 1); lcd.print(" "); lcd.setCursor(13, 1); lcd.print((int) (stepsToMm(sledPos) + 0.5)); lcd.setCursor(13, 2); lcd.print(" "); lcd.setCursor(13, 2); lcd.print(targetMm); update = false; } if (encoderRotate()) { if (knobLeft) targetMm--; else targetMm++; if (targetMm < 0) targetMm = 0; if (targetMm > MAX_SLED_DISTANCE) targetMm = MAX_SLED_DISTANCE; update = true; } if (button4()) { delay(200); // debounce wireStepper.enable(); long targetPos = mmToSteps(targetMm); if (targetPos > sledPos) { while (targetPos >= sledPos && !button4() && !endstopLeft()) { wireStepper.move(M2STEPS); sledPos += M2STEPS; } } else { while (targetPos <= sledPos && !button4() && !endstopRight()) { wireStepper.move(-M2STEPS); sledPos -= M2STEPS; } } wireStepper.disable(); delay(250); // debounce update = true; } } } screen = S_SETUP; break; } // Change paramenters case S_PARAMS: { Serial.println("Set wire parameters"); clearLCD(); lcd.setCursor(0, 0); lcd.print("-- Set wire para. --"); lcd.setCursor(0, 1); lcd.print("Wire mm: "); lcd.setCursor(0, 3); lcd.print("[<-] Back"); bool update = true; while (!button1()) { if (update) { lcd.setCursor(9, 1); lcd.print(" "); lcd.setCursor(9, 1); lcd.print(stepsToMm(stepsPerTurn), 3); update = false; } if (encoderRotate()) { if (knobLeft) stepsPerTurn--; else stepsPerTurn++; if (stepsPerTurn < 0) stepsPerTurn = 0; if (stepsPerTurn > maxSledSteps) stepsPerTurn = maxSledSteps; update = true; } } delay(200); // debounce screen = S_SETUP; break; } // Auto home sled case S_HOME: { Serial.println("Auto home"); clearLCD(); lcd.noBlink(); lcd.setCursor(0, 0); lcd.print(" -- Auto home -- "); lcd.setCursor(0, 2); lcd.print(" Please wait.. "); sledPos = -1; homePosition(); sledPos = 0; screen = S_SETUP; break; } // Wind a coil case S_WIND: { Serial.println("Wind coil"); clearLCD(); lcd.noBlink(); lcd.setCursor(0, 0); lcd.print(" -- Wind coil -- "); if (stepsPerTurn == 0) { lcd.setCursor(0, 1); lcd.print("Set wire size "); lcd.setCursor(0, 2); lcd.print("first! "); lcd.setCursor(0, 3); lcd.print("[<-] Back"); while (!button1()); delay(200); // debounce } else { lcd.setCursor(0, 1); lcd.print("Tr TOTAL "); lcd.setCursor(0, 2); lcd.print("mm TOTAL "); lcd.setCursor(0, 3); lcd.print(" [<-] Back "); // Move coilstepper to "zero" position coilStepper.enable(); while (!enteredBarrier()) coilStepper.move(M1STEPS); delay(500); coilStepper.disable(); long turns = 0; long totalTurns = 0; int turnStep = 50; boolean isRunning = false; boolean update = true; while (!button1()) { if (update) { lcd.setCursor(3, 1); lcd.print(" "); lcd.setCursor(3, 1); lcd.print(turns); lcd.setCursor(15, 1); lcd.print(" "); lcd.setCursor(15, 1); lcd.print(totalTurns); lcd.setCursor(3, 2); lcd.print(" "); lcd.setCursor(3, 2); lcd.print(stepsToMm(turns * stepsPerTurn), 1); lcd.setCursor(15, 2); lcd.print(" "); lcd.setCursor(15, 2); lcd.print(stepsToMm(totalTurns * stepsPerTurn), 1); lcd.setCursor(12, 3); if (isRunning) lcd.print("[Stop] "); else lcd.print("[Start]"); update = false; } if (encoderRotate()) { if (knobLeft) turns -= turnStep; else turns += turnStep; if (turns < 0) turns = 0; if (turns > 9999) turns = 9999; update = true; } if (encoderPress()) { if (turnStep == 50) turnStep = 1; else turnStep = 50; delay(200); // debounce } if (button4()) { isRunning = !isRunning; if (isRunning) { wireStepper.enable(); wireStepper.setRPM(S_WIND_COIL_RPM); coilStepper.enable(); coilStepper.setMicrostep(M1STEPS); } else { wireStepper.disable(); coilStepper.disable(); } delay(400); // debounce update = true; } if (isRunning) { if (endstopLeft() || turns <= 0) { wireStepper.disable(); coilStepper.disable(); isRunning = false; } else { //while(!enteredBarrier()) coilStepper.move(M1STEPS); while(!enteredBarrier()) { coilStepper.move(M1STEPS); delay(5); } wireStepper.move(stepsPerTurn); // anschließend Drahtmotor Anzahl Schritte, welche in etwa der Drahtdicke entsprechen turns--; totalTurns++; } update = true; } } } delay(200); // debounce screen = S_MAIN; break; } case S_BOBBIN: { Serial.println("Wind bobbin"); clearLCD(); lcd.noBlink(); lcd.setCursor(0, 0); lcd.print(" -- Wind bobbin -- "); if (stepsPerTurn == 0) { lcd.setCursor(0, 1); lcd.print("Set wire size "); lcd.setCursor(0, 2); lcd.print("first! "); lcd.setCursor(0, 3); lcd.print("[<-] Back"); while (!button1()); delay(200); // debounce } else { lcd.setCursor(0, 1); lcd.print("Tr TOTAL "); lcd.setCursor(0, 2); lcd.print("Set Tpl "); lcd.setCursor(0, 3); lcd.print("Dir [Start] "); // Move coilstepper to "zero" position coilStepper.enable(); while (!enteredBarrier()) coilStepper.move(M1STEPS); delay(500); coilStepper.disable(); long turns = 0; long totalTurns = 0; int turnStep = 50; boolean isRunning = false; boolean update = true; boolean dirLeft = true; boolean setMode = true; long turnsPerLayer = 0; long turnsPerLayerCounter = 0; while (!button1()) { if (update) { lcd.setCursor(4, 1); lcd.print(" "); lcd.setCursor(4, 1); lcd.print(turns); lcd.setCursor(15, 1); lcd.print(" "); lcd.setCursor(15, 1); lcd.print(totalTurns); lcd.setCursor(15, 2); lcd.print(" "); lcd.setCursor(15, 2); lcd.print(turnsPerLayerCounter); lcd.setCursor(4, 2); if (setMode) lcd.print("ON "); else lcd.print("OFF"); lcd.setCursor(4, 3); if (dirLeft) lcd.print(""); lcd.setCursor(12, 3); if (isRunning) lcd.print("[Stop] "); else lcd.print("[Start]"); update = false; } if (encoderRotate()) { if (knobLeft) turns -= turnStep; else turns += turnStep; if (turns < 0) turns = 0; if (turns > 9999) turns = 9999; update = true; } if (encoderPress()) { if (turnStep == 50) turnStep = 1; else turnStep = 50; delay(200); // debounce } if (button2()) { setMode = !setMode; if (setMode) { turnsPerLayerCounter = 0; turnsPerLayer = 0; } else { dirLeft = !dirLeft; } delay(300); // debounce update = true; } if (button3()) { dirLeft = !dirLeft; delay(200); // debounce update = true; } if (button4()) { isRunning = !isRunning; if (isRunning) { wireStepper.enable(); wireStepper.setRPM(S_WIND_COIL_RPM); coilStepper.enable(); coilStepper.setMicrostep(M1STEPS); } else { wireStepper.disable(); coilStepper.disable(); } delay(400); // debounce update = true; } if (isRunning) { if (endstopLeft() || endstopRight() || turns <= 0) { wireStepper.disable(); coilStepper.disable(); isRunning = false; } else { while (!enteredBarrier()) coilStepper.move(M1STEPS); // anschließend Drahtmotor Anzahl Schritte, welche in etwa der Drahtdicke entsprechen if (dirLeft) Serial.print("<"); if (!dirLeft) Serial.print(">"); if (dirLeft) wireStepper.move(stepsPerTurn); else wireStepper.move(-stepsPerTurn); turnsPerLayerCounter++; turns--; totalTurns++; // Im Set mode addieren bzw. ansonsten Anzahl Wicklungen/pro Schicht überwachen if (setMode) { turnsPerLayer++; } else { if (turnsPerLayerCounter >= turnsPerLayer) { turnsPerLayerCounter = 0; dirLeft = !dirLeft; Serial.print("R"); } } } update = true; } } // while } // else delay(2000); screen = S_MAIN; break; } // About screen case S_ABOUT: { Serial.println("About screen"); clearLCD(); lcd.noBlink(); lcd.setCursor(0, 0); lcd.print(" Coil winding "); lcd.setCursor(0, 1); lcd.print(" machine software "); lcd.setCursor(0, 2); lcd.print(" v1.4 (2021-05-03) "); lcd.setCursor(0, 3); lcd.print("by ldiecks@gmail.com"); delay(4000); screen = S_MAIN; break; } } }