Automatic winding machine

Good morning. I have used the search function here but have not yet found a good final solution. I hope that you can give me a few tips on how to implement this setup.

I would like to create the following test environment:

  • Arduino Mega2560
  • one stepper motor C17HD2024-01N
  • one TMC2130 stepper driver
  • Two spring-loaded micro switches
  • One 10k potentiometer (analogue)
  • trapezoidal threaded rod
  • Slide for trapezoidal thread

This test setup should satisfy this condition:

The carriage should travel a distance "x" (variable) forwards and backwards. The carriage is driven by the stepper motor, which is connected to the trapezoidal threaded rod.

When the carriage hits the micro switch 2, the stepper motor should stop with a delay and rotate in the opposite direction with a further delay. When the carriage hits micro switch 1, the same happens as above.

I would like to use the MoBa Tools database for the Arduino code. Unfortunately, with my currently limited Arduino knowledge, I do not yet know how to integrate the micro button without always having to press it.

I have used this code as "inspiration":
(not yet completely changed)

#define MAX8BUTTONS // spart Speicher, da nur 4 Taster benötigt werden (saves RAM)
#include <MobaTools.h>
const byte dirPin       = 5;
const byte stepPin      = 6;
const byte enaPin       = 7;
const byte button1Pin   = A1;
const byte button2Pin   = A2;
const byte potPin       = A0; 


const int STEPS_REVOLUTION = 3200;
MoToStepper myStepper( STEPS_REVOLUTION, STEPDIR );

enum { Button1=0, Button2 } ; 
const byte buttonPins[] = { button1Pin, button2Pin };
MoToButtons button( buttonPins, sizeof(buttonPins), 20, 500 );

MoToTimebase speedIntervall;    
int vspeed = 0;                 //Steppergeschwindigkeit in U/min*10

void setup()
{
  myStepper.attach( stepPin, dirPin );
  myStepper.attachEnable( enaPin, 10, LOW ); 
  myStepper.setSpeed( 200 );
  myStepper.setRampLen( 100 );                     
  speedIntervall.setBasetime( 100 );             
}

void loop() {
  button.processButtons();   

  if ( speedIntervall.tick() ) {
    vspeed = map((analogRead(potPin)), 0, 1023, 20, 1800);
    myStepper.setSpeed( vspeed );
  }

  if (button.pressed(Button1) ) {
    myStepper.rotate( 1 );          // Stepper dreht vorwärts
  }
  if ( button.released(Button1) ) {
    //Taster1 losgelassen
    myStepper.rotate(0);             // Stepper stoppt
  }

  if (button.pressed(Button2) ) {
    //Taster2 gedrĂĽckt
    myStepper.rotate( -1 );         // Stepper dreht rückwärts
  }
  if ( button.released(Button2) ) {
    //Taster2 losgelassen 
    myStepper.rotate(0);            // Stepper stoppt
  }
}

What starts the 'travel' of the carriage and what defines the distance 'x'?

What are these 'delay'? Does it mean it goes beyond the micro switch? Is 'delay' a time or a distance?

You should explain a bit better what happens.
How is the sequence started/stopped. What can be set and how (speed/distance/direction). Under what conditions are the limit switches reached.
Maybe a picture of the setup will help.

Sorry, that's quite unclear to me, what do you mean with "without always having to press it"? Based on your description, it looks like the switches are being pressed each time the carriage reaches an end, how comes the button won't be pressed?
Could you describe the real target of your project? I mean, what is the project usage? Why you seem having the need for carriage going forward and backwards? And does it just need to go from one end to the other, or some other kind of movements in between?

And, as I say most of the times, when you have a project to build always start with smaller sketches doing single things at the time, to better understand how to handle the situation. Just as an example, connect only two pushbuttons (the end buttons) and two leds (replacing the stepper, representing the direction: turn led 1 on if the direction is left and led 2 if right, and both off if stopped), and simulating the ends by pressing the buttons by yourself. You could even do that with any online simulator like Wokwi or Tinkercad, so I strongly suggest you to do so, and then posting back here the result in case you need help or more hints from us.

PS: translating the comments into english could help us much more on understanding your code...

That is correct. For an outsider, a few important details are still missing.

My son and I want to get into the Arduino world. One topic we have decided on is coil winding for loudspeakers.

A perfect result should be achieved through constant learning and trial and error. You can find complex and elaborate coil winders on the Internet. It should be a simple learning project with a practical use. I have already finalised the coil drive here:

The slider is a linear wire tracking system. This is moved to the right by the stepper and the threaded rod. The trapezoidal thread has a fixed length.

The coil winder should be variable in order to realise different coil widths. The slider therefore does not always travel the same distance from left to right (distance is called coil width). The variable end stop (coil end) is converted / set with micro switch 2.

Micro switch 1 is permanently mounted and represents the start of the coil. This micro switch is pressed at the start. This should define point zero. The slider (i.e. the wire tracking) should start with a delay. The rotary movement of the threaded rod should function like a ramp. Rise slowly but steadily to the set speed. This is to prevent the wire from tracking too early.

The same should happen when the slider moves into the end position (end of spool) and presses micro switch 2. The speed of the threaded rod should decrease / slow down until it comes to a standstill. The direction of rotation should now be reversed and the speed of rotation should also increase steadily (ramp).

If the slider now moves back towards micro switch 1, the same should happen as with micro switch 2. The slider is sent back and forth between the two micro switches until the coil is full.

Sorry, that's quite unclear to me, what do you mean with "without always having to press it"? Based on your description, it looks like the switches are being pressed each time the carriage reaches an end, how comes the button won't be pressed?

I was referring to the code template.
The stepper only moves when the button is pressed. In my case, it should only report back a "message" ==> " Slow down, stop, change direction of rotation and accelerate".

Maybe a picture of the setup will help.

The parts are in 3D printing and I can't upload a picture yet where you can see how it should look.

I hope I haven't forgotten anything and that the project is now a little more transparent.

Both of these cannot be true. Have you confused switch 1 and switch 2 in one of the statements

Thanks, that was a mistyping!

According to my weak knowledge and together with the template I have written this code:

#define MAX8BUTTONS //
#include <MobaTools.h>

const byte dirPin       = 5;                         // definiert Pin D5 (OUT) an Stepperdriver DIR - defines pin D5 (OUT) on stepperdriver DIR
const byte stepPin      = 6;                         // definiert Pin D6 (OUT) an Stepperdriver STEP - defines pin D6 (OUT) on stepperdriver STEP
const byte enaPin       = 7;                         // definiert Pin D7 (OUT) an Stepperdriver ENABLE - defines pin D7 (OUT) on stepperdriver ENABLE
const byte button1Pin   = A1;                        // definiert Pin A1 (IN) Analogeingang
const byte button2Pin   = A2;                        // definiert Pin A2 (IN) Analogeingang
const byte potPin       = A0;                        // definiert Pin A0 (IN) Analogeingang / Poti

const int STEPS_REVOLUTION = 3200;                   // Stepper einrichten
MoToStepper myStepper( STEPS_REVOLUTION, STEPDIR );  // 3200 Microschritte / Umdrehung - 3200 microsteps / revolution

enum { Button1=0, Button2 } ;                        
const byte buttonPins[] = { button1Pin, button2Pin };
MoToButtons button( buttonPins, sizeof(buttonPins), 20, 500 );

MoToTimebase speedIntervall                          // the speed pot ist read only every 'speedintervall' ms
int vspeed = 0;                                      // Steppergeschwindigkeit in U/min*10 - Stepper speed in rpm*10

void setup() {
  myStepper.attach( stepPin, dirPin );
  myStepper.attachEnable( enaPin, 500, LOW );        // Enable Pin ( LOW=aktiv ) , Deceleration 500ms 
  myStepper.setSpeed( 500 );
  myStepper.setRampLen( 1600 );                      // Ramp 1600 Microsteps @ 20U/min
  speedIntervall.setBasetime( 100 );  
}

void loop() {
	
  button.processButtons(); 
   
  if ( speedIntervall.tick() ) {
    vspeed = map((analogRead(potPin)), 0, 1023, 20, 3200); 
    myStepper.setSpeed( vspeed );
  }

  // Bedingung 1: Taster 1 gedrĂĽckt - Function 1: Button 1 pressed
  
  if button.pressed(Button1) {          // Steppermotor im Uhrzeigersinn anlaufen lassen mit Rampe (Verzögerung 500ms)
	                                    // Start the stepper motor clockwise with ramp (delay 500ms)
    stepper.rotate(1, 500);
  } else {
    
    stepper.rotate(1);                  // Steppermotor weiter drehen lassen 
	                                    // Keep the stepper motor turning  
  }

  // Bedingung 2: Taster 2 gedrĂĽckt - Function 2: Button 2 pressed
  
  if button.pressed(Button2) {          // Steppermotor abbremsen (Verzögerung 500ms) und Drehrichtung umkehren
                                        // Decelerate stepper motor (deceleration 500ms) and reverse direction of rotation
    stepper.rotate(0, 500);
    stepper.rotate(-1, 500);
  } else {                              // Steppermotor weiter gegen den Uhrzeigersinn drehen lassen
                                        // Allow the stepper motor to continue turning counter-clockwise
    stepper.rotate(-1);
  }
}

Why did you start a new topic? I think this is one project, and in the end there should be one sketch that controls the coil drive and the linear tracking system. And I think the speed of the coil drive and the speed of the tracking system should be syncronized. So both belong together and should be discussed in one topic - so every paticipant knows about the whole project.

Initially, I wanted to operate both drives separately. The initial aim is not to build a fully functional automated machine. The pure project development and the failures and successes have priority one. My son is only 16 years old and he also understands. The message here should be "one step at a time" ... or better: "Learning by doing"

Did you try to compile?

rotate only takes one argument. What do you want to achive here?

The best way to realize what you want is a finite state machine ( or Zustandsautomat ).

Yes, that's ok.

  1. Get the the coil drive working,
  2. Get the tracking system working
  3. Put both together.

But from the beginning you should have the third step in mind :wink:
So you can learn a lot :sunglasses: - and you will get a lot of help here.

1 Like

That's a good age. I think I had my first contact with electronics even a little earlier (microprocessors didn't exist back then). Later I made it my profession. Now I'm retired and want to inspire ( and help ) others.

1 Like

It doesn't look like it - there are C++ errors and misuse of the MobaTools library functions. Many of them have clear compilation error messages - just compile it again and again, fixing the first error each time. Shouldn't take very long and then you can see if it actually works.

1 Like

I took this from the MoBa Tool manual:

myStepper.rotate( int direction );

I thought that the ramp in the brackets comes after the direction. I guess I was wrong.

I'll have to have another look at the ramp (i.e. acceleration and deceleration).

The error message gives you a very good impression of where your own forgetfulness is already at an advanced stage. :joy:

Forgotten brackets, forgotten commas, upper and lower case, correct naming.
Now.... the code was accepted / verified. :grinning:

The initially cleaned up code - untested!

#define MAX8BUTTONS
#include <MobaTools.h>

const byte dirPin       = 5;                         // definiert Pin D5 (OUT) an Stepperdriver DIR - defines pin D5 (OUT) on stepperdriver DIR
const byte stepPin      = 6;                         // definiert Pin D6 (OUT) an Stepperdriver STEP - defines pin D6 (OUT) on stepperdriver STEP
const byte enaPin       = 7;                         // definiert Pin D7 (OUT) an Stepperdriver ENABLE - defines pin D7 (OUT) on stepperdriver ENABLE
const byte button1Pin   = A1;                        // definiert Pin A1 (IN) Analogeingang
const byte button2Pin   = A2;                        // definiert Pin A2 (IN) Analogeingang
const byte potPin       = A0;                        // definiert Pin A0 (IN) Analogeingang / Poti

const int STEPS_REVOLUTION = 3200;                   // Stepper einrichten
MoToStepper myStepper( STEPS_REVOLUTION, STEPDIR );  // 3200 Microschritte / Umdrehung - 3200 microsteps / revolution

enum { Button1=0, Button2 } ;                        
const byte buttonPins[] = { button1Pin, button2Pin };
MoToButtons button( buttonPins, sizeof(buttonPins), 20, 500 );

MoToTimebase speedIntervall;                          // the speed pot ist read only every 'speedintervall' ms
int vspeed = 0;                                      // Steppergeschwindigkeit in U/min*10 - Stepper speed in rpm*10

void setup() {
  myStepper.attach( stepPin, dirPin );
  myStepper.attachEnable( enaPin, 500, LOW );        // Enable Pin ( LOW=aktiv ) , Deceleration 500ms 
  myStepper.setSpeed( 500 );
  myStepper.setRampLen( 100 );                      // Ramp 1600 Microsteps @ 20U/min
  speedIntervall.setBasetime( 100 );  
}

void loop() {
	
  button.processButtons(); 
   
  if ( speedIntervall.tick() ) {
    vspeed = map((analogRead(potPin)), 0, 1023, 20, 1800); 
    myStepper.setSpeed( vspeed );
  }

  // Bedingung 1: Taster 1 gedrĂĽckt
  // Function 1: Button 1 pressed
  
  if (button.pressed(Button1) ) {                       // Steppermotor im Uhrzeigersinn anlaufen lassen mit Rampe (Verzögerung 500ms)
	                                                 // Start the stepper motor clockwise with ramp (delay 500ms)
    myStepper.rotate(1);
  } else {
    
    myStepper.rotate(1);                               // Steppermotor weiter drehen lassen 
	                                                 // Keep the stepper motor turning  
  }

  // Bedingung 2: Taster 2 gedrĂĽckt
  // Function 2: Button 2 pressed
  
  if (button.pressed(Button2)) {                       // Steppermotor abbremsen (Verzögerung 500ms) und Drehrichtung umkehren
                                                     // Decelerate stepper motor (deceleration 500ms) and reverse direction of rotation
    myStepper.rotate(0);
    myStepper.rotate(-1);
  } else {                                           // Steppermotor weiter gegen den Uhrzeigersinn drehen lassen
                                                     // Allow the stepper motor to continue turning counter-clockwise
    myStepper.rotate(-1);
  }
}

I see only a single int being passed to the rotate() function

Sorry, what do you mean? I don't follow you (yet).

EDIT:

I think I understand it.I have to reverse the command.
If my stepper motor is to rotate, then it must not be pressed. Only when the command to reverse is given must it be pressed.

From the MoBa Tool manual:

myStepper.rotate( int direction );

There is only a single parameter wheresas you used 2

stepper.rotate(1, 500);

You keep going on with your current code, instead of following what @MicroBahner correctly suggested you since post #10.

You should start doing "one step at a time" (at least the three steps listed there), and learn how finite state machines work and how can be implemented for Arduino.
Otherwise you'll keep going on doing things wrong and/or confuse your mind.

1 Like

Well, as already suggested you should have a look at the FSM. Maybe it takes some time until you know how it works, but you will save that time later when creating the sketch, because its so much easier with a FSM.
You must think in 'states', 'events' and 'actions' - the main principles of a FSM.
What is very important: only the statements of the active 'state' are executed - all statements of other states are ignored at that time.
E.g.

  • state: moving forward and waiting for an event (that the limit switch is beeing pressed )
    The event happens ( switch is closing), now you take actions:
    • reversing the direction of movement
    • switch state to moving backwards (what means from this time on other statements will be executed!)

Now, in the new state you check for the other limit switch.

And there must be other states or events. How will you stop and start the movement?

By the way, the 'switch' statement is a perfect means of realising a state machine. Depending on the switch variable (which represents the 'states') different sets of statemens are executed.

Some hints about your sketch/comments:

'500' in this case means 500ms between the last step and disabling the stepper. This has nothing to do with decelerating

The comment doesn't fit to the parameter :wink:

That doesn't really make sense - doing the same in the if and the else block

That doesn't make sense either. rotate(0) is directly overwritten with rotate(-1). effectively rotate(0) is not executed ( and not needed if you want to change the direction of movement).

Here is a rough framework for your state machine with switch:

  switch (stepperState) {
    //-------------------- stepper stopped ---------------------------------
    case IDLE: // stepper in stop, waiting for start
      if ( startevent ) { // whatever startevent is  ...
        stepper.rotate(1);
        stepperState = MOVING_CW; // stepper now moving forward
      }
      break;
    //-------------------- stepper moving forward ----------------------------------
    case MOVING_CW: // moving towards switch 2
      if ( digitalRead(switch2) == LOW ) {
        // limit switch activated, changing direction
        stepper.rotate(-1); // decelerating and accelerating is done automatically
        stepperState = MOVING CCW; // stepper now moving backwards
      }
      if ( stopevent ) { // whatever may stop everything
        stepper.rotate(0);
        stepperState = IDLE;
      }
      break;
    //-------------------- stepper moving backwards ----------------------------------
    case MOVING_CCW: // moving towards switch 1
      if ( digitalRead(switch1) == LOW ) {
        // limit switch activated, changing direction
        stepper.rotate(1); // decelerating and accelerating is done automatically
        stepperState = MOVING CW; // stepper now moving forward
      }
      if ( stopevent ) { // whatever may stop everything
        stepper.rotate(0);
        stepperState = IDLE;
      }
      break;
    //------------------------------------------------------
   }

Of course the events will not really work that way - it's only a principle. And it depends how you check the limit switches. You can do with a simple digitalRead, because with that state machine, you don't even need debouncing of your limit switches.
If anything is unclear ( I suppose there may be something :wink: ) - feel free to ask

[Edit] updated framework to make state blocks more clear.

1 Like

While I agree that an FSM solution would be good here, do be aware that they are unnaturally difficult to understand the first time you see them.

You may want to fix up the code you have to learn how to use the library for motors and read buttons. It’ll be a good learning experience and you can graduate to the more esoteric solution later.