Startracker/main.py

198 lines
5.1 KiB
Python
Raw Normal View History

2023-11-21 09:18:22 +00:00
"""
Skytracker by Nick Touran for ESP8266 and Stepper Motors
Port by Jon Saul <jonsaul@puppetmaster.lol>
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. """
import machine
2023-11-21 09:18:22 +00:00
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
2023-11-21 09:18:22 +00:00
# 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 = [
2023-11-24 20:56:23 +00:00
Pin(14, Pin.OUT), # D5
Pin(12, Pin.OUT), # D6
Pin(13, Pin.OUT), # D7
Pin(15, Pin.OUT) # D8
2023-11-21 09:18:22 +00:00
]
2023-11-24 22:19:18 +00:00
MODE_PIN = Pin(5, Pin.IN) # D7
2023-11-21 09:18:22 +00:00
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]
2023-11-21 09:18:22 +00:00
]
step_delta = 0
step_num = 0
total_seconds = 0.0
totalsteps = 0
2023-11-21 09:18:22 +00:00
step_interval_s = 0.001
current_step = 0
2023-11-24 22:19:18 +00:00
nxt = 0
2023-11-21 09:18:22 +00:00
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)
2023-11-21 09:18:22 +00:00
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.
"""
2023-11-24 22:19:18 +00:00
if current_mode == NORMAL:
step_interval_s = 1.0/(ROTATIONS_PER_CM*ypt(total_seconds)*2*DOUBLESTEPS_PER_ROTATION)
step_delta = 1
step_num %= len(STEPPER_SEQUENCE)
return
elif current_mode == REWINDING:
step_interval_s = 0.0025
step_delta = -2
if(step_num<0):
step_num += len(STEPPER_SEQUENCE)
return
elif current_mode == STOPPED:
step_interval_s = 0.2
return
else:
print("we should never be here")
2023-11-21 09:18:22 +00:00
2023-11-24 22:19:18 +00:00
if current_mode != STOPPED:
total_seconds += step_interval_s
current_step = STEPPER_SEQUENCE[step_num]
do_step(current_step)
step_num += step_delta
totalsteps += step_delta
now = time.now()
nxt = now+step_interval_s*CYCLES_PER_SECOND-(now-nxt);
timer = Timer(2)
timer.init(mode=Timer.ONE_SHOT, period=nxt)
def _debounce(pin):
cur_value = pin.value()
active = 0
while active < 20:
if pin.value() != cur_value:
active += 1
else:
active = 0
time.sleep(1.0)
2023-11-21 09:18:22 +00:00
def do_step(current_step):
# apply a single step of the stepper motor on its pins.
for i in NUM_PINS:
if current_step[i] is 1:
2023-11-24 20:56:23 +00:00
motorPins[i].value(1)
else:
motorPins[i].value(0)
2023-11-21 09:18:22 +00:00
def setup():
setup_gpio()
setup_timer()
2023-11-24 23:00:17 +00:00
if not MODE_PIN.value():
print("Manual REWIND")
autostop = False
current_mode = REWINDING
2023-11-21 09:18:22 +00:00
def setup_timer():
machine.disable_irq()
timer = Timer(2)
timer.init(freq=1000)
timer.callback(lambda t:step_motor())
machine.enable_irq()
2023-11-21 09:18:22 +00:00
def setup_gpio():
2023-11-24 23:00:17 +00:00
all_pins_off()
MODE_PIN.irq(trigger=Pin.IRQ_LOW_LEVEL, handler=toggle_mode)
2023-11-24 20:56:23 +00:00
def all_pins_off(pin):
2023-11-24 22:24:03 +00:00
for i in len(motorPins):
2023-11-24 20:56:23 +00:00
pin.value(0)
2023-11-21 09:18:22 +00:00
2023-11-21 09:18:22 +00:00
def toggle_mode():
# We have several modes that we can toggle between with a button,
# NORMAL, REWIND, and STOPPED.
# Need to find replacement for ESP.getCycleCount();
#if ESP.getCycleCount() - last_toggle < 0.2*CYCLES_PER_SECOND:
# return
2023-11-24 22:19:18 +00:00
_debounce(MODE_PIN)
if current_mode is REWINDING:
print("STOPPING")
current_mode = STOPPED
all_pins_off()
if not autostop:
step_num = 0
total_seconds = 0
totalsteps = 0
autostop = True
elif current_mode is NORMAL:
print("REWINDING")
current_mode = REWINDING
else:
print("RESTARTING")
current_mode = NORMAL
last_toggle = 0# FIND REPLACEMENT FOR ESP.getCycleCount();
2023-11-21 09:18:22 +00:00
pass
2023-11-24 22:24:03 +00:00
while True:
if current_mode == REWINDING:
if(totalsteps<1 and autostop):
print("Ending the rewind and stopping")
current_mode = STOPPED
all_pins_off()
total_seconds = 0.0