// Pico-SDK specific libraries #include #include #include #include "hardware/gpio.h" #include "hardware/pwm.h" // C libraries #include #include // Local libraries #include "leds.hpp" #include "driver.hpp" #include "angles.hpp" // Communication #define BUFFER_LENGTH 10 /* Commands: 1st bit = command 2nd bit = data size rest n bits = data */ #define COMMAND_STOP 0x00 // Returns: Nothing #define COMMAND_ANGLE 0x01 // Returns: Nothing #define COMMAND_R 0x02 // Returns: Actual angle and r (floats as char*) #define COMMAND_X_COORDINATE 0x03 // Returns: Nothing #define COMMAND_Y_COORDINATE 0x04 // Returns: --- #define COMMAND_HOME 0x05 // Returns: Nothing #define COMMAND_UPDATE_ANGLE 0x06 // Returns: Nothing #define COMMAND_UPDATE_R 0x07 // Returns: Nothing #define COMMAND_M1_STEP 0x08 // Returns: Actual angle and r (floats as char*) #define COMMAND_M2_STEP 0x09 // Returns: Actual angle and r (floats as char*) #define COMMAND_SET_MOTOR_SPEED 0x0a // Returns: Nothing #define COMMAND_SET_LED_TRACK 0x0b // Returns: Nothing #define COMMAND_SET_LED_SPEED 0x0c // Returns: Nothing #define COMMAND_SET_LED_INTENSITY 0x0d // Returns: Nothing #define COMMAND_SET_LED_SATURATION 0x0e // Returns: Nothing #define COMMAND_SET_LED_R 0x0f // Returns: Nothing #define COMMAND_SET_LED_G 0x10 // Returns: Nothing #define COMMAND_SET_LED_B 0x11 // Returns: Nothing #define COMMAND_SET_LED_W 0x12 // Returns: Nothing #define COMMAND_GET_ANGLE 0x13 // Returns: Angle (float as char*) #define COMMAND_GET_R 0x14 // Returns: R (float as char*) #define COMMAND_RESET 0x15 // Returns: Nothing // LED PINS #define R_PIN 25 // PWM4 B - D2 #define G_PIN 7 // PWM3 B - D10 #define B_PIN 21 // PWM2 B - D9 #define W_PIN 17 // PWM0 B - D5 led leds(R_PIN, G_PIN, B_PIN, W_PIN); // Motors #define MOTORS_ENABLE 20 // D8 #define M1_DIR 18 // D6 #define M1_STEP 15 // D3 #define M1_SENSOR 6 // D13 #define M2_DIR 19 // D7 #define M2_STEP 16 // D4 #define M2_SENSOR 4 // D12 motor motor1(M1_DIR, M1_STEP, MOTORS_ENABLE, M1_SENSOR); motor motor2(M2_DIR, M2_STEP, MOTORS_ENABLE, M2_SENSOR); // LED settings int led_speed = 50000; absolute_time_t led_time = get_absolute_time(); float led_intensity = 0.4; float led_saturation = 0.0; /* LED tracks: 0 = static color 1 = red pulse 2 = green pulse 3 = blue pulse 4 = white pulse 5 = RGB fade */ int led_track = 0; // Motor settings int spr = 600; int microstepping = 16; float theta1_old = 0; float theta2_old = M_PI; float angle_old = 0; float r_old = 0; bool inverted = false; int draw_speed = 1; /* Second core function */ void second_core() { while (1) { float angle = 0.0; float r = 0.0; int fifo = 0; fifo = multicore_fifo_pop_blocking(); motor1.Enable(); // Home command if (fifo == COMMAND_HOME) { home(motor1, motor2); theta1_old = 0.0; theta2_old = M_PI; angle_old = 0; r_old = 0; multicore_fifo_push_blocking(0); continue; } // M1 steps else if (fifo == COMMAND_M1_STEP) { int steps = multicore_fifo_pop_blocking(); motor1.setDirection(steps); motor1.Step(abs(steps), 5); multicore_fifo_push_blocking(0); continue; } // M2 steps else if (fifo == COMMAND_M2_STEP) { int steps = multicore_fifo_pop_blocking(); motor2.setDirection(steps); motor2.Step(abs(steps), 5); multicore_fifo_push_blocking(0); continue; } // Theta nd R else { fifo = multicore_fifo_pop_blocking(); angle = *reinterpret_cast(&fifo); fifo = multicore_fifo_pop_blocking(); r = *reinterpret_cast(&fifo); } // If r is zero ignore angle if (r == 0) { angle = angle_old; } // If r is too much - ignore if (r > 1.0) { multicore_fifo_push_blocking(1); continue; } // If delta r is too much - ignore else if (abs(r - r_old) > 0.05) { multicore_fifo_push_blocking(1); continue; } // Getting angles for the arms float theta2 = polarGetTheta2(angle, r); float theta1 = polarGetTheta1(theta2, angle, r, inverted, theta1_old); // Getting the change of angles float delta_theta1 = deltaAngles(theta1, theta1_old); float delta_theta2 = deltaAngles(theta2, theta2_old); // Getting the steps needed int step1 = steps(delta_theta1, microstepping); int step2 = steps(delta_theta2, microstepping); // Accounting the arm2 spin step2 += step1; // Setting the direction of the motors motor1.setDirection(step1); motor2.setDirection(step2); // Moving the motors if ((abs(step1) > abs(step2)) && (step2 != 0)) { dualSteps(abs(step1), motor1, abs(step2), motor2, draw_speed); } else if ((abs(step1) < abs(step2)) && (step1 != 0)) { dualSteps(abs(step2), motor2, abs(step1), motor1, draw_speed); } else if (abs(step1) == abs(step2)) { equalSteps(abs(step1), motor2, motor1, draw_speed); } else if (step2 == 0 && step1 != 0) { motor1.Step(abs(step1), draw_speed); } else if (step1 == 0 && step2 != 0) { motor2.Step(abs(step2), draw_speed); } // Get arm angles from the steps theta1_old += thetaFromSteps(step1, microstepping); theta2_old += thetaFromSteps(step2 - step1, microstepping); // Check if angles are over 2PI if (theta1_old >= (2*M_PI)) { theta1_old -= (2*M_PI); } else if (theta1_old <= -(2*M_PI)) { theta1_old += (2*M_PI); } angle_old = thetaFromArms(theta1_old, theta2_old); r_old = rFromArms(theta1_old, theta2_old); multicore_fifo_push_blocking(1); } } /* Read serial input */ uint16_t read_serial(uint8_t *buffer) { uint16_t buffer_index = 0; while (1) { int c = getchar_timeout_us(100); if (c != PICO_ERROR_TIMEOUT && buffer_index < BUFFER_LENGTH) { buffer[buffer_index++] = c; } else { break; } } return buffer_index; } /* Combine n number of bytes to float */ float combine_float_bytes(uint8_t *bytes) { uint32_t value = 0; int n = bytes[0]; for (int i = 1; i <= n; i++) { value |= bytes[i] << (8 * (i - 1)); } return *reinterpret_cast(&value); } /* Combine n number of bytes to int */ int combine_int_bytes(uint8_t *bytes) { int result = 0; int n = bytes[0]; for (int i = 1; i <= n; i++) { result |= static_cast(bytes[i]) << ((8 * (i - 1))); } return result; } /* Splits float into bytes for serial */ void split_float_to_bytes(float value, unsigned char* bytes) { unsigned char* int_bytes = reinterpret_cast(&value); for (int i = 0; i < 4; i++) { bytes[i] = *(int_bytes + i); } } int main() { // Initialize serial stdio_init_all(); // Loop while serial not connected while(!stdio_usb_connected()) { leds.OneColorFade(0); sleep_us(led_speed); } // Turn off LEDs and reset the counter leds.Off(); leds.counter = 0; // Launch the second core multicore_launch_core1(second_core); // Main function variables float angle = 0.0; float r = 0.0; float x = 0.0; float y = 0.0; while (1) { // Buffer for serial commands uint8_t buf[BUFFER_LENGTH] = {0xfe}; read_serial(&buf[0]); // Commands switch (buf[0]) { // Stop case COMMAND_STOP: { motor1.Stop(); break; } // Angle first case COMMAND_ANGLE: { angle = combine_float_bytes(&buf[1]); break; } // R second case COMMAND_R: { r = combine_float_bytes(&buf[1]); // TODO: safeguard to check if error in data multicore_fifo_push_blocking(COMMAND_R); multicore_fifo_push_blocking(*reinterpret_cast(&angle)); multicore_fifo_push_blocking(*reinterpret_cast(&r)); break; } // X coordnate first case COMMAND_X_COORDINATE: { x = combine_float_bytes(&buf[1]); break; } // Y coordnate second case COMMAND_Y_COORDINATE: { y = combine_float_bytes(&buf[1]); // TODO: Convert cartesian to polar... break; } // Home case COMMAND_HOME: { multicore_fifo_push_blocking(COMMAND_HOME); break; } // Update angle first case COMMAND_UPDATE_ANGLE: { angle_old = combine_float_bytes(&buf[1]); break; } // Update r second case COMMAND_UPDATE_R: { r_old = combine_float_bytes(&buf[1]); theta2_old = polarGetTheta2(angle_old, r_old); theta1_old = polarGetTheta1(theta2_old, angle_old, r_old, inverted, angle_old); break; } // M1 step case COMMAND_M1_STEP: { multicore_fifo_push_blocking(COMMAND_M1_STEP); int steps = combine_int_bytes(&buf[1]); multicore_fifo_push_blocking(steps); break; } // M2 step case COMMAND_M2_STEP: { multicore_fifo_push_blocking(COMMAND_M2_STEP); int steps = combine_int_bytes(&buf[1]); multicore_fifo_push_blocking(steps); break; } // Set motor speed case COMMAND_SET_MOTOR_SPEED: { draw_speed = combine_int_bytes(&buf[1]); break; } // Set led track case COMMAND_SET_LED_TRACK: { led_track = combine_int_bytes(&buf[1]); break; } // Set led speed case COMMAND_SET_LED_SPEED: { led_speed = combine_int_bytes(&buf[1]) * 1000; break; } // Set led intensity case COMMAND_SET_LED_INTENSITY: { float value = combine_float_bytes(&buf[1]); if (value > 1.0) { value = 1.0; } else if (value <= 0.0) { value = 0.00001; } leds.intensity = value; break; } // Set led saturation case COMMAND_SET_LED_SATURATION: { float value = combine_float_bytes(&buf[1]); if (value > 1.0) { value = 1.0; } else if (value <= 0.0) { value = 0.0; } leds.saturation = value; break; } // Set LED R case COMMAND_SET_LED_R: { led_track = 0; float value = (float)(combine_int_bytes(&buf[1])); if (value > 255.0) { value = 255.0; } else if (value <= 0.0) { value = 0.0; } value /= 255.0; value *= leds.cycle_wrap; leds.SetValue(0, (int)(value)); break; } // Set LED G case COMMAND_SET_LED_G: { led_track = 0; float value = (float)(combine_int_bytes(&buf[1])); if (value > 255.0) { value = 255.0; } else if (value <= 0.0) { value = 0.0; } value /= 255.0; value *= leds.cycle_wrap; leds.SetValue(1, (int)(value)); break; } // Set LED B case COMMAND_SET_LED_B: { led_track = 0; float value = (float)(combine_int_bytes(&buf[1])); if (value > 255.0) { value = 255.0; } else if (value <= 0.0) { value = 0.0; } value /= 255.0; value *= leds.cycle_wrap; leds.SetValue(2, (int)(value)); break; } // Set LED W case COMMAND_SET_LED_W: { led_track = 0; float value = (float)(combine_int_bytes(&buf[1])); if (value > 255.0) { value = 255.0; } else if (value <= 0.0) { value = 0.0; } value /= 255.0; value *= leds.cycle_wrap; leds.SetValue(3, (int)(value)); break; } // Get Angle case COMMAND_GET_ANGLE: { printf("%f\n", angle_old); } // Get R case COMMAND_GET_R: { printf("%f\n", r_old); } // Reset core 1 case COMMAND_RESET: { multicore_reset_core1(); multicore_launch_core1(second_core); } } // Listening to core 1 uint32_t core1_msg = 0; if (multicore_fifo_rvalid()) { multicore_fifo_pop_timeout_us(100, &core1_msg); } // Returning the angle and r when motors are done if (core1_msg == 1) { printf("%f\n", angle_old); fflush(stdout); printf("%f\n", r_old); fflush(stdout); } // Trigger the LED track if(absolute_time_diff_us(led_time, get_absolute_time()) > led_speed) { led_time = get_absolute_time(); if (led_track == 1) { leds.OneColorFade(0); } else if (led_track == 2) { leds.OneColorFade(1); } else if (led_track == 3) { leds.OneColorFade(2); } else if (led_track == 4) { leds.OneColorFade(3); } else if (led_track == 5) { leds.ColorFade(); } } } }