Section 10: Motor Setup
Let’s continue setting up the motor in our sketch on line 168 of MotorControl.h. Here we will learn about more Trinamic parameters to set up.
// Start all the UART communications functions behind the scenes
driver.begin();
//For operation with StealthChop, this parameter is not used, but it is required to enable the motor. In case of operation with StealthChop only, any setting is OK
driver.toff(4);
//Recommended blank time select value
driver.blank_time(24);
// Disbaled to use the extrenal current sense resistors
driver.I_scale_analog(false);
// Use the external Current Sense Resistors. Do not use the internal resistor as it can't handle high current.
driver.internal_Rsense(false);
//Microstep resolution selected by MSTEP register and NOT from the legacy pins.
driver.mstep_reg_select(true);
// Set the current in milliamps
driver.rms_current(current);
//Set the stall value from 0-255. Higher value will make it stall quicker
driver.SGTHRS(stall);
//Set the number of microsteps. the TMC2209 uses MicroPlyer in order to turn every step into 256 microsteps. However, it’s not as accurate when you want to be precise. So set the microsteps as high as you can, and if your motor is not going as fast as you want it to, decrease the microsteps to 128, 64, 32, 16, 8, 4, 2. or 1. I like to keep 64 because it allows for a high top speed. However, your IC (ESP32) bust be able to send a very fast pulse for higher microstepping.
driver.microsteps(motor_microsteps);
// Minimum speed at which point to turn on StallGuard. StallGuard does not work as very low speeds such as the beginning of acceleration so we need to keep it off until it reaches a reliable speed.
driver.TCOOLTHRS(tcools);
// DisableStealthChop PWM mode/ Page 25 of datasheet
driver.TPWMTHRS(0);
// Turn off smart current control, known as CoolStep. It's a neat feature but is more complex and messes with StallGuard.
driver.semin(0);
driver.shaft(true); // Set the shaft direction clockwise.
driver.shaft(false); // Set the shaft direction counter-clockwise.
// Disable SpreadCycle. We want StealthChop becuase it works with StallGuard.
driver.en_spreadCycle(false);
// Enable UART control
driver.pdn_disable(true);
// Set the ESP32 pin that will toggle the direction of the motor
stepper->setDirectionPin(DIR_PIN);
// Set the ESP32 pin that will enable and disable the motor driver. We usually want the driver disabled when it's not running. The downside of this is that the motor will spin freely. If you need it to hold the position, do not disable the driver.
stepper->setEnablePin(ENABLE_PIN);
// The FastAccelStepper library has a neat "Auto Enable" feature that automatically enables and disables your board for you. When it needs to be moved it will enable the board, and when the movement is complete it will disable it. Again, when disabled the motor will spin freely.
stepper->setAutoEnable(true);
Loop Setup
Now that everything is set up, let’s review the loop which will call the functions to make the motor move. This method might seem strange, but it serves a purpose so please pay attention
We are not going to use the traditional void loop() that Arduino uses. Instead we are using the loop we created on Core 1 for our motor to use. Remember that the FastAccelLibrary uses all of the resources of the core when moving, which is a problem with maintaining a WiFi connection.
In the Void MotorTask function, we have a loop that checks for many if statements.
If the variable run_motor == true, then the motor function will begin. We will get back to this in a minute.
Else if the variable set_zero == 1, then the setZero() funtion will get executed. This function resets the position of the motor back to 0.
Else if the variable wifi_button == true, then the button_change() function gets executed. This function resets the WiFi password that's stored on your device.
Else if nothing happens, just do a delay of 1 tick using the vTaskDelay(1) call. This is slightly more advanced but the entire code base of the ESP32 is built on FreeRTOS which uses ticks for timing. I highly recommend you learn about it if you want to get serious. The entire Arduino core hides all of this, but in the end everything is running on FreeRTOS.
There are several ways we can trigger run_motor to be true. One way is to use the buttons. The two buttons on the board are connected to interrupts which you can see on Line 47 of MotorControl.h
If a button is pressed, it simply changes the variable which the MotorTask loop sees and triggers the move_motor() function to begin. Before we look at the move_motor() function that makes your motor move, we need to understand positioning.
Understanding Positioning
When we tell the motor to move to position 5000 or 50000, what does that mean? How does that translate to a value we can understand? In the image below we have a blue object on a track. The left side is position 0 and the right side is position 5000. Whenever we tell a position, imagine your motor moving an object like this.
The value 5000 is based on step count. Remember the section about microsteps where we discussed how many steps are in one revolution.
When we tell the motor to move 5000 steps we are telling the motor how many microsteps to move. If we have 256 microsteps set it will require many more steps to move 1 meter than if we have 1 microstep.
But using setps to move is very difficult to understand as a distance, so we need something we can actually understand like inches or centimeters. In this case I will work with inches which you can convert to different units.
If we attach a rope or belt to the motor and want it to move 1 inch, how many steps will we need to move?
First, that depends on your microstep setting!
Second, it depends on the diameter of your pulley.
Assuming a 64 microstep setting and 1 inch diameter pulley on the motor, in order to move 1 inch, we need to move
Since there are 200 steps per revolution and 64 microsteps per steps, that give us a total of 12,800 steps per revolution.
One full revolution of a 1 inch diameter pulley will move the pulley a total or 3.14159 inches. This can be figured out by converting diameter to circumference.
2pi x 0.5 inches = 3.14159 inches
Imagine this 1 inch ball rolling down the arrow. It it does one revolution it will have moved 3.14159 inches.
12,800 / 3.14159 = 4,074 steps. This is the number of steps we need to command to more our pulley one inch.
move_motor() Function Walkthrough
Now that we understand how positioning works and how to trigger the motor in loop, let's walk through the actual function that makes the motor turn.
Go to line 99 on the MotorControl.h page.
When you see "stepper->" this means we are accessing the FastAccelStepper library and passing values to it. It will then control the stepper motor.
We begin by setting the current position of the motor. The library automatically monitors its position so it should always remember where it is. However, I like to do this just in case we issued a command somewhere else to reset the position to 0, such as reset the home position.
stepper->setCurrentPosition(current_position);
We reset our interrupt variables to false, this is just in case they did not get reset elsewhere.
stalled_motor = false;
sensor1_trip = false;
sensor2_trip = false;
We set the acceleration
stepper->setAcceleration(accel);
We set the max_speed
stepper->setSpeedInHz(max_speed);
We set the current
driver.rms_current(current);
Set the stall value
driver.SGTHRS(stall);
Set the tcools value
driver.TCOOLTHRS(tcools);
The reason for setting these values each time is because they might have been changed somewhere else. By setting them every time we guarantee the correct settings each time.
Next we need to do some math to figure out which direction the motor will turn. The reason for this is because sometimes moving in different directions will require different settings. For example, if you want to close a sliding door, it may take more force to open it versus closing it. So you will need to set different currents for the different actions.
If the motor is already at the position we told it to go to, we want it to exit from this function and not do anything which is why we make this if statement first.
if (current_position == move_to_step)
Next we check if the move_to_step variable, which is the position we want it to move to is greater than the current position. If so, that means we want the window, door, curtain, etc, to open
else if (move_to_step > current_position) // Open
Then we check the opposite, if this is true then we want the device to move in the opposite direction and close
else if (move_to_step < current_position) // Close
Or else if none of those things are true then do nothing and just exist the loop.
else // Nothing
What happens if the current position is 0 and the commanded position is 5000?
current_position = 0 and move_to_step = 5000
We will enter this statement
else if (move_to_step > current_position) // Open
Next, we command the FastAccelLibrary to begin moving right away to position 5000
stepper->moveTo(move_to_step);
But what if something goes when our device is closing. What if something gets in the way of our sliding door while it closes?
We need a way to check for a stall condition and stop the motor if there is a stall.
This loop only runs while the motor is running and has NOT reached its destination
while (stepper->getCurrentPosition() != stepper->targetPos())
How are we alerted to a stall? We created an interrupt that makes the stalled_motor variable true.
if (stalled_motor == true)
Stop the motor right away. Do not decelerate, just stop it now!
stepper->forceStop();
Then break out of the loop
break;
Then set the current_position variable to the position that is stored in the library.
current_position = stepper->getCurrentPosition();
In this case the motor will either end up moving to the position we told it to, or it will stall and immediately stop for an emergency.
After the function is complete, we are not done yet!
What called the move_motor function to begin with? The MotorTask loop did. Let's go back to line 188 of the Firmware tab. We now set run_motor = false which will take us out of the if condition. Then we update ESPUI with the current position of the motor. More on ESPUI later.
NEXT SECTION: 11
Course Sections:
- Section 0: Background
- Section 1: Hardware Setup
- Section 2: Stepper Motor Basics
- Section 3: Arduino Setup
- Section 4: Understanding Trinamic Drivers
- Section 5: Setting Trinamic Drivers
- Section 6: Power Requirements
- Section 7: StallGuard
- Section 8: FastAccelStepper Library
- Section 9: ESP32 Dual Core Setup
- Section 10: Motor Setup
- Section 11: Preferences Library
- Section 12: ESPUI
- Section 13: API