Firmware Design

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.


Initialization

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.


Homing

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.


Decoding Serial

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.


Primary Loop and Movement

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.