// -*- mode: c++; -*- // STARTRACKER MOTOR CONTROL: STEPPER MOTOR CONTROL FOR JJROBOTS POV DISPLAY // This code is designed for JJROBOTS arDusplay Stepper Motor Control board // Author: JJROBOTS.COM (Jose Julio & Juan Pedro) // Licence: GNU GPL // Stepper : NEMA17 // Driver : A4988 or DRV8825 // Microstepping : configured to 1/16 // Arduino board: Pro micro (leonardo equivalent) #include static const unsigned int led_pin = 13; static const unsigned int step_pin = 3; static const unsigned int dir_pin = 2; static const unsigned int m0_pin = 6; static const unsigned int m1_pin = 5; static const unsigned int m2_pin = 4; static const unsigned int enable_pin = led_pin; // using a 200-step motor (most common) // pins used are DIR, STEP, MS1, MS2, MS3 in that order //A4988 stepper(200, 8, 9, 10, 11, 12); DRV8825 stepper(200, dir_pin, step_pin, enable_pin, m0_pin, m1_pin, m2_pin); // set to -1/1 to select rotation static const bool stepper_direction = true; #include // For things that could have real impact on execution #ifndef DEBUG #define DEBUG (0) #endif // For very light debug that has *no* chance to interfere // significatively with execution #ifndef WEAK_DEBUG #define WEAK_DEBUG (1) #endif #if DEBUG #define dprint(x) Serial.println(x) #else #define dprint(x) #endif #if WEAK_DEBUG #define dwprint(x) Serial.println(x) #else #define dwprint(x) #endif //other info needed: //ratio between the large gear and the small one=0.2549 // Science here ! static const float nr_teeth_small = 11.0; static const float nr_teeth_big = 53.0; static const float axis_hinge_dist_mm = 200; // Use immediate value. Using symbolic values leads to incorrect value. static const float earth_rot_speed_rad_msec = 7.272205e-8; //2*PI / (1440*60); static const float bolt_thread_mm = 1.25; //static const float coef = 2*PI*axis_hinge_dist_mm * nr_teeth_big / (bolt_thread_mm * nr_teeth_small); static const unsigned int microstepping_div = 16; static const unsigned int nr_steps = 200 * microstepping_div; static const float stepper_gear_rad_per_step = (2*PI) / nr_steps; // this needs to be reset static struct rot_state_t { unsigned long elapsed_time_millis; float stepper_gear_rot_rad = 0; } rot_state; #define DUMP(v) do { \ Serial.print(#v " "); \ Serial.println(v, 10); \ } while(0) static void debug_long(rot_state_t *s){ const unsigned long ellapsed_in_sec = s->elapsed_time_millis/1000; DUMP(ellapsed_in_sec); DUMP(earth_rot_speed_rad_msec); DUMP(axis_hinge_dist_mm); DUMP(nr_teeth_big); DUMP(nr_teeth_small); DUMP(bolt_thread_mm); DUMP(PI); } static float get_expected_stepper_rot(rot_state_t *s) { const unsigned long ellapsed_in_sec = s->elapsed_time_millis/1000; const float r = tan(earth_rot_speed_rad_msec * s->elapsed_time_millis /* ellapsed_in_sec */) * axis_hinge_dist_mm * 2 * PI * nr_teeth_big / (bolt_thread_mm * nr_teeth_small); #if DEBUG debug_long(s); Serial.print("Angle final: "); Serial.println(r); #endif return r; } static unsigned int get_step_number(rot_state_t *s, float expected_rotation) { const float angle_diff = expected_rotation - s->stepper_gear_rot_rad; const float fsteps = angle_diff / stepper_gear_rad_per_step; const int steps = floor(fsteps); #if DEBUG Serial.print("diff :"); Serial.print(angle_diff, 6); Serial.print(" needed steps : "); Serial.print(steps); Serial.print(" with fsteps: "); Serial.println(fsteps); #endif return steps; } static void set_stepper_rotation(rot_state_t *s, float angle){ const unsigned int needed_steps = get_step_number(s, angle); if (stepper_direction) { stepper.move(needed_steps); } else { stepper.move(-needed_steps); } s->stepper_gear_rot_rad += needed_steps * stepper_gear_rad_per_step; } static unsigned int loop_count = 0; static const unsigned int btn1_pin = 8; Switch button1Switch = Switch(btn1_pin); static const unsigned int btn2_pin = 5; static const unsigned int btn3_pin = 6; static const unsigned int end_stop_pin = 10; static const unsigned long serial_speed = 115200UL; #define ENABLE_LED_BLINK (0) #define USE_ACTIVE_WAIT (1) static const int use_active_wait = USE_ACTIVE_WAIT; static const long active_threshold = 10; static struct { unsigned long period; unsigned long deadline; long remain; unsigned int expired; } active_timer; static unsigned long global_period = 50; // BIT functions // #define CLR(x,y) (x&=(~(1< RUN_OR_RESET"); } break; case RUN_OR_RESET: { STATE(RUN_OR_RESET); unsigned long reset_delay = millis() + 500; NEXT_STATE(RUN); // stepper will be used in both exit states stepper.enable(); while(millis() < reset_delay) { button1Switch.poll(); if (button1Switch.pushed()) { NEXT_STATE(RESET_POSITION); dwprint("Pushed RUN_OR_RESET => RESET_POSITION"); break; } } if (control_state == RUN) { // means not pushed during waiting time dwprint("NOT Pushed RUN_OR_RESET => RUN"); start_timer(global_period); } break; } case RUN: STATE(RUN); if (button1Switch.pushed()) { dwprint("Short press RUN => IDLE"); stop_timer(); stepper.disable(); NEXT_STATE(IDLE); } else if (active_timer.expired) { active_timer.expired--; // emit_motor_step(); // step_motor(); rot_state.elapsed_time_millis += active_timer.period; const float expected_rot = get_expected_stepper_rot(&rot_state); set_stepper_rotation(&rot_state, expected_rot); #if ENABLE_LED_BLINK blink_led(); #endif } break; case RESET_POSITION: { STATE(RESET_POSITION); unsigned long debounce_reset = millis(); int reset_done = 0; while(!reset_done) { if (stepper_direction) { stepper.move(-1); } else { stepper.move(1); } int level = digitalRead(end_stop_pin); if ( level == HIGH) { debounce_reset = millis(); } else { if (millis() - debounce_reset > 50) { reset_done = 1; } } } NEXT_STATE(IDLE); stepper.disable(); dwprint("Finished RESET, => IDLE"); break; } } #if DEBUG == 2 Serial.println("End of Automata step..."); #endif } void setup() { // debug output Serial.begin(serial_speed); dwprint("Serial setup"); // Set target motor RPM to 1RPM stepper.setRPM(200); // Set full speed mode (microstepping also works for smoother hand movement stepper.setMicrostep(microstepping_div); dwprint("Microstepping is"); dwprint(microstepping_div); stepper.disable(); // Setup PIN as GPIO output pinMode(led_pin, OUTPUT); // LED pin // Button input with pullups enable // pinMode(btn1_pin, INPUT); pinMode(btn2_pin, INPUT_PULLUP); pinMode(btn3_pin, INPUT_PULLUP); pinMode(end_stop_pin, INPUT_PULLUP); // Initial setup for motor driver // digitalWrite(led_pin, HIGH); // delay(200); // Initial delay // digitalWrite(led_pin, LOW); dwprint("Setup finished, starting loop"); } void loop(void) { if (use_active_wait) handle_active_timer(); control_automata(); }