#include #include #include // For M_PI and mathematical operations #define rotInterfaceType AccelStepper::DRIVER #define inOutInterfaceType AccelStepper::DRIVER #define stepPin_rot 2 #define dirPin_rot 5 #define stepPin_InOut 3 #define dirPin_InOut 6 #define rot_total_steps 16000.0 #define inOut_total_steps 5760.0 #define BUFFER_SIZE 10 // Maximum number of theta-rho pairs in a batch // Create stepper motor objects AccelStepper rotStepper(rotInterfaceType, stepPin_rot, dirPin_rot); AccelStepper inOutStepper(inOutInterfaceType, stepPin_InOut, dirPin_InOut); // Create a MultiStepper object MultiStepper multiStepper; // Buffer for storing theta-rho pairs float buffer[BUFFER_SIZE][2]; // Store theta, rho pairs int bufferCount = 0; // Number of pairs in the buffer bool batchComplete = false; // Track the current position in polar coordinates float currentTheta = 0.0; // Current theta in radians float currentRho = 0.0; // Current rho (0 to 1) bool skipInterpolation = true; void setup() { // Set maximum speed and acceleration rotStepper.setMaxSpeed(5000); // Adjust as needed rotStepper.setAcceleration(5000); // Adjust as needed inOutStepper.setMaxSpeed(5000); // Adjust as needed inOutStepper.setAcceleration(5000); // Adjust as needed // Add steppers to MultiStepper multiStepper.addStepper(rotStepper); multiStepper.addStepper(inOutStepper); // Initialize serial communication Serial.begin(115200); Serial.println("READY"); homing(); } void resetThetaToNearestMultiple() { currentTheta = 0; skipInterpolation = true; // Set flag to skip interpolation for the next movement } void loop() { // Check for incoming serial commands or theta-rho pairs if (Serial.available() > 0) { String input = Serial.readStringUntil('\n'); // Ignore invalid messages if (input != "HOME" && input != "RESET_THETA" && !input.endsWith(";")) { Serial.println("IGNORED"); return; } if (input == "RESET_THETA") { resetThetaToNearestMultiple(); // Reset currentTheta Serial.println("THETA_RESET"); // Notify Python Serial.println("READY"); return; } if (input == "HOME") { homing(); return; } // If not a command, assume it's a batch of theta-rho pairs if (!batchComplete) { int pairIndex = 0; int startIdx = 0; // Split the batch line into individual theta-rho pairs while (pairIndex < BUFFER_SIZE) { int endIdx = input.indexOf(";", startIdx); if (endIdx == -1) break; // No more pairs in the line String pair = input.substring(startIdx, endIdx); int commaIndex = pair.indexOf(','); // Parse theta and rho values float theta = pair.substring(0, commaIndex).toFloat(); // Theta in radians float rho = pair.substring(commaIndex + 1).toFloat(); // Rho (0 to 1) buffer[pairIndex][0] = theta; buffer[pairIndex][1] = rho; pairIndex++; startIdx = endIdx + 1; // Move to next pair } bufferCount = pairIndex; batchComplete = true; } } // Process the buffer if a batch is ready if (batchComplete && bufferCount > 0) { // Start interpolation from the current position float startTheta = currentTheta; float startRho = currentRho; for (int i = 0; i < bufferCount; i++) { if (skipInterpolation) { // float targetRotSteps = buffer[0][0] * (rot_total_steps / (2.0 * M_PI)); // rotStepper.setCurrentPosition(targetRotSteps); currentTheta = buffer[0][0]; // Move only the in-out axis to the first coordinate's rho movePolar(buffer[0][0], buffer[0][1]); // Reset the flag after the first movement skipInterpolation = false; } else { // Use interpolation for subsequent movements interpolatePath( startTheta, startRho, buffer[i][0], buffer[i][1], 0.001 // Step size ); } // Update the starting point for the next segment startTheta = buffer[i][0]; startRho = buffer[i][1]; } bufferCount = 0; // Clear buffer batchComplete = false; // Reset batch flag Serial.println("READY"); } } void homing() { Serial.println("HOMING"); // Move inOutStepper inward for homing inOutStepper.setSpeed(-5000); // Adjust speed for homing while (true) { inOutStepper.runSpeed(); if (inOutStepper.currentPosition() <= -inOut_total_steps * 1.1) { // Adjust distance for homing break; } } inOutStepper.setCurrentPosition(0); // Set home position currentTheta = 0.0; // Reset polar coordinates currentRho = 0.0; Serial.println("HOMED"); } void movePolar(float theta, float rho) { // Convert polar coordinates to motor steps long rotSteps = theta * (rot_total_steps / (2.0 * M_PI)); // Steps for rot axis long inOutSteps = rho * inOut_total_steps; // Steps for in-out axis // Calculate offset for inOut axis float revolutions = theta / (2.0 * M_PI); // Fractional revolutions (can be positive or negative) long offsetSteps = revolutions * 1600; // 1600 steps inward or outward per revolution // Apply the offset to the inout axis inOutSteps += offsetSteps; // Define target positions for both motors long targetPositions[2]; targetPositions[0] = rotSteps; targetPositions[1] = inOutSteps; // Move both motors synchronously multiStepper.moveTo(targetPositions); multiStepper.runSpeedToPosition(); // Blocking call // Update the current coordinates currentTheta = theta; currentRho = rho; } void interpolatePath(float startTheta, float startRho, float endTheta, float endRho, float stepSize) { // Calculate the total distance in the polar coordinate system float distance = sqrt(pow(endTheta - startTheta, 2) + pow(endRho - startRho, 2)); int numSteps = max(1, (int)(distance / stepSize)); // Ensure at least one step for (int step = 0; step <= numSteps; step++) { float t = (float)step / numSteps; // Interpolation factor (0 to 1) float interpolatedTheta = startTheta + t * (endTheta - startTheta); float interpolatedRho = startRho + t * (endRho - startRho); // Move to the interpolated theta-rho movePolar(interpolatedTheta, interpolatedRho); } }