From f9e588e0cf262c92fdf8bf39106ab6e1ae1b904d Mon Sep 17 00:00:00 2001 From: dogma Date: Tue, 21 Nov 2023 03:18:22 -0600 Subject: [PATCH] Initial commit and start of porting --- boot.py | 1 + main.py | 112 ++++++++++++++++++++ motorcontroller.ino | 221 +++++++++++++++++++++++++++++++++++++++ pymakr.conf | 10 ++ workspace.code-workspace | 12 +++ 5 files changed, 356 insertions(+) create mode 100644 boot.py create mode 100644 main.py create mode 100644 motorcontroller.ino create mode 100644 pymakr.conf create mode 100644 workspace.code-workspace diff --git a/boot.py b/boot.py new file mode 100644 index 0000000..b8e7bb0 --- /dev/null +++ b/boot.py @@ -0,0 +1 @@ +# boot.py -- run on boot-up diff --git a/main.py b/main.py new file mode 100644 index 0000000..7eff2f1 --- /dev/null +++ b/main.py @@ -0,0 +1,112 @@ +""" + Skytracker by Nick Touran for ESP8266 and Stepper Motors + Port by Jon Saul + This accelerates the motor to correct the tangent error. It can rewind too! + + Motors are 28BYJ-48 5V + ULN2003 Driver Board from Amazon + Hook up power and ground and then hook inputs 1-4 up to GPIO pins. + + See Also: http://www.raspberrypi-spy.co.uk/2012/07/stepper-motor-control-in-python/ + This motor has a 1:64 gear reduction ratio and a stride angle of 5.625 deg (1/64th of a circle). + + So it takes 64*64 = 4096 single steps for one full rotation, or 2048 double-steps. + with 3 ms timing, double-stepping can do a full rotation in 2048*0.003 = 6.144 seconds + so that's a whopping 1/6.144 * 60 = 9.75 RPM. But it has way more torque than I expected. + + Can get 2ms timing going with double stepping on ESP8266. Pretty fast! + Should power it off of external 5V. + + Shaft diameter is 5mm with a 3mm inner key thing. Mounting holes are 35mm apart. """ + +from machine import Pin +from machine import Timer +import math +import time + +NUM_PINS = 4 +NUM_STEPS = 8 +RADS_PER_SEC = 7.292115e-05 +LENGTH_CM = 28.884 # fill in with precise measured value +# For theta zero, I used relative measurement between two boards w/ level. +# Got 0.72 degrees, which is 0.012566 radians +THETAO = 0.012566 +ROTATIONS_PER_CM = 7.8740157 +DOUBLESTEPS_PER_ROTATION = 2048.0 +CYCLES_PER_SECOND = 80000000 + +# Modes +NORMAL = 0 +REWINDING = 1 +STOPPED = 2 + +motorPins = [ + Pin(16, Pin.out), # D0 + Pin(5, Pin.out), # D1 + Pin(4, Pin.out), #D2 + Pin(14, Pin.out) #D5 +] + +MODE_PIN = Pin(13, Pin.out) #D7 + +STEPPER_SEQUENCE = [ + [1,0,0,1], + [1,0,0,0], + [1,1,0,0], + [0,1,0,0], + [0,1,1,0], + [0,0,1,0], + [0,0,1,1], + [0,0,0,1] +] + +step_delta = 0 +step_num = 0 +total_seconds = 0.0 +totalsteps = 0 +step_interval_s = 0.001 +current_step = 0 +next = 0 +now = 0 +last_toggle = 0 +current_mode = NORMAL +autostop = True + + +def ypt(ts): + # Bolt insertion rate in cm/s: y'(t) + # Note, if you run this for ~359 minutes, it goes to infinity!! + return LENGTH_CM * RADS_PER_SEC/math.pow(math.cos(THETAO + RADS_PER_SEC * ts),2) + +def step_motor(): + """ + This is the callback function that gets called when te timer + expires. It moves the motor, updates lists, recomputes + the step interval based on the current tangent error, + and sets a new timer. + """ + pass + +def do_step(current_step): + # apply a single step of the stepper motor on its pins. + pass +def setup(): + pass +def setup_timer(): + pass +def setup_gpio(): + for i in NUM_PINS: + Pin(motorPins[i], Pin.OUT) + all_pins_off() + button = Pin(MODE_PIN, Pin.IN) + button.irq(trigger=Pin.IRQ_FALLING, handler=toggle_mode) + pass +def all_pins_off(): + for i in NUM_PINS: + Pin(motorPins[i], Pin.low()) + +def toggle_mode(): + # We have several modes that we can toggle between with a button, + # NORMAL, REWIND, and STOPPED. + pass +def loop(): + pass \ No newline at end of file diff --git a/motorcontroller.ino b/motorcontroller.ino new file mode 100644 index 0000000..250be82 --- /dev/null +++ b/motorcontroller.ino @@ -0,0 +1,221 @@ + +/* + * Skytracker by Nick Touran for ESP8266 and Stepper Motors + * + * partofthething.com + * + * This accelerates the motor to correct the tangent error. It can rewind too! + * + * Motors are 28BYJ-48 5V + ULN2003 Driver Board from Amazon + * Hook up power and ground and then hook inputs 1-4 up to GPIO pins. + * + * See Also: http://www.raspberrypi-spy.co.uk/2012/07/stepper-motor-control-in-python/ + * This motor has a 1:64 gear reduction ratio and a stride angle of 5.625 deg (1/64th of a circle). + * + * So it takes 64*64 = 4096 single steps for one full rotation, or 2048 double-steps. + * with 3 ms timing, double-stepping can do a full rotation in 2048*0.003 = 6.144 seconds + * so that's a whopping 1/6.144 * 60 = 9.75 RPM. But it has way more torque than I expected. + * + * Can get 2ms timing going with double stepping on ESP8266. Pretty fast! + * Should power it off of external 5V. + * + * Shaft diameter is 5mm with a 3mm inner key thing. Mounting holes are 35mm apart. + * + */ + + +#define NUM_PINS 4 +#define NUM_STEPS 8 +#define RADS_PER_SEC 7.292115e-05 +#define LENGTH_CM 28.884 // fill in with precise measured value +// For theta zero, I used relative measurement between two boards w/ level. +// Got 0.72 degrees, which is 0.012566 radians +#define THETA0 0.012566 // fill in with angle at fully closed position (radians) +#define ROTATIONS_PER_CM 7.8740157 // 1/4-20 thread +#define DOUBLESTEPS_PER_ROTATION 2048.0 +#define CYCLES_PER_SECOND 80000000 + +//modes +#define NORMAL 0 +#define REWINDING 1 +#define STOPPED 2 + + +int allPins[NUM_PINS] = {D1, D2, D3, D4}; +int MODE_PIN = D7; + +// from manufacturers datasheet +int STEPPER_SEQUENCE[NUM_STEPS][NUM_PINS] = {{1,0,0,1}, + {1,0,0,0}, + {1,1,0,0}, + {0,1,0,0}, + {0,1,1,0}, + {0,0,1,0}, + {0,0,1,1}, + {0,0,0,1}}; + +int step_delta; +int step_num = 0; +double total_seconds = 0.0; +long totalsteps = 0; +double step_interval_s=0.001; +int *current_step; +volatile unsigned long next; // next time to trigger callback +volatile unsigned long now; // volatile keyword required when things change in callbacks +volatile unsigned long last_toggle; // for debounce +volatile short current_mode=NORMAL; +bool autostop=true; // hack for allowing manual rewind at boot + +float ypt(float ts) { + // bolt insertion rate in cm/s: y'(t) + // Note, if you run this for ~359 minutes, it goes to infinity!! + return LENGTH_CM * RADS_PER_SEC/pow(cos(THETA0 + RADS_PER_SEC * ts),2); +} + +void inline step_motor(void) { + /* This is the callback function that gets called when the timer + * expires. It moves the motor, updates lists, recomputes + * the step interval based on the current tangent error, + * and sets a new timer. + */ + switch(current_mode) { + case NORMAL: + step_interval_s = 1.0/(ROTATIONS_PER_CM * ypt(total_seconds)* 2 * DOUBLESTEPS_PER_ROTATION); + step_delta = 1; // single steps while filming for smoothest operation and highest torque + step_num %= NUM_STEPS; + break; + case REWINDING: + // fast rewind + step_interval_s = 0.0025; // can often get 2ms but gets stuck sometimes. + step_delta = -2; // double steps going backwards for speed. + if (step_num<0) { + step_num+=NUM_STEPS; // modulus works here in Python it goes negative in C. + } + break; + case STOPPED: + step_interval_s = 0.2; // wait a bit to conserve power. + break; + } + + if (current_mode!=STOPPED) { + total_seconds += step_interval_s; // required for tangent error + current_step = STEPPER_SEQUENCE[step_num]; + do_step(current_step); + step_num += step_delta; // double-steppin' + totalsteps += step_delta; + } + + // Serial.println(totalsteps); + // Before setting the next timer, subtract out however many + // clock cycles were burned doing all the work above. + now = ESP.getCycleCount(); + next = now + step_interval_s * CYCLES_PER_SECOND - (now-next); // will auto-rollover. + timer0_write(next); // see you next time! +} + +void do_step(int *current_step) { + /* apply a single step of the stepper motor on its pins. */ + for (int i=0;i