Puckinator Firmware Overview: Source Code
Our firmware’s purpose was to receive two stepper motor angles via serial communication and robustly implement the movement of the striker attached to the steppers to a desired coordinate on the air hockey table. It has three primary functionalities: homing, emergency stop, and manual debugging mode. We used the FlexyStepper library to implement homing and facilitate stepper movement.
We begin by declaring all of our constants and creating two instances of FlexyStepper: one called stepper1 and one called stepper2, where stepper1 is the left motor and stepper2 is the right. In the setup function, we initialize each stepper instance with their pin values, speed, acceleration, and the de facto number of steps per revolution. Our mechanical system consisted of two Nema 23 Stepper Motors each with 1600 steps per revolution and a gear ratio of 3. Our resultant number of steps per resolution was therefore the product of the two, or 1600 * 3.
The function moveToHomeInRevolutions()
from FlexyStepper has four inputs: a direction
toward home, a homing speed in revolutions per second, a max homing distance in revolutions, and the
pin number of a limit switch. Once given these inputs, FlexyStepper moves each motor instance until
it hits a limit switch, at which point it sets stepper.moveToHomeInRevolutions()
to
true
(stepper
here is a placeholder for an instance name).
For stepper1, we set a direction of 1 to move the motor counterclockwise at a speed 0.1 revolutions
per second until it hit its associated limit switch at the pin LIMIT_SWITCH_PIN_1
. For
stepper2, we set a direction of -1 to move the motor clockwise at the same speed until it hit the
limit switch at LIMIT_SWITCH_PIN_2
.
While the function values returned false, pin 11 would light up to signify homing failure. This is a feature implemented for debugging and does not offer any functionality.
Once both sequences were complete and the function values were set to true
, we moved
each stepper to an experimentally determined revolution value so that the affixed arms were parallel
to each other and perpendicular to the short edge of the air hockey table. Then, we set the new
position as 0, which is the equivalent of 90 degrees in the inverse kinematics coordinate frame
mentioned in Software. This effectively standardized the stepper motor position, allowing us to
consistently transfer the calculated angles from python to stepper motor revolution values. It also
means miscalibration caused by accidents in which the stepper motor skips steps can be easily
remedied through homing.
The Arduino, which is initialized with the same baud rate (115200) set in software, continually
receives the desired theta and phi values as a string in the format “θ,φ”
. This is
decoded using the built-in C function strtok()
, which breaks down the string into two
smaller strings ”θ”
and ”φ”
once given the delimiter ”,”
. We
then convert the string to a double-precision floating-point value, which is divided by 2π to
convert into revolutions. Each angle value is then fed into the FlexyStepper function
setTargetPositionInRevolutions()
, which does not actually move the motor but does set
the intended position of the motor. The converted theta value is fed to stepper1, while the
converted phi value is fed to stepper2.
In the primary loop, we call the function processIncomingByte()
which decodes serial and
sets the target positions of the motors in revolutions. Once the decoding process is entirely
complete and the serial port is empty, we call the FlexyStepper function
processMovement()
, which moves the stepper one step in the direction of movement if the
FlexyStepper function motionComplete()
returns true. This was an important design
decision in greatly reducing our latency, as explained in the Major Design Decisions section.