Skip to content

Commit

Permalink
Got motor and servo modules working
Browse files Browse the repository at this point in the history
  • Loading branch information
ZodiusInfuser committed Aug 21, 2023
1 parent 0427fa3 commit bf1f6e5
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 53 deletions.
76 changes: 76 additions & 0 deletions examples/yukon_adaptive_big_motor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import time
import math
from pimoroni_yukon import Yukon
from pimoroni_yukon.modules import BigMotorModule

SPEED = 5 # The speed that the motors will cycle at
UPDATES = 50 # How many times to update LEDs and Servos per second
MOTOR_EXTENT = 0.4 # How far from zero to drive the motors

# Create a Yukon object to begin using the board
yukon = Yukon(logging_level=6)

# List to store the modules
motor_modules = []

import tca

def print_tca(message=""):
print(f"---- {message} -----")
#print(f"Stored Dir = [0]{tca.stored_config(0):0{16}b} [1]{tca.stored_config(1):0{16}b}")
print(f"True Dir = [0]{tca.read_config(0):0{16}b} [1]{tca.read_config(1):0{16}b}")
print()
#print(f"Stored Out = [0]{tca.stored_output(0):0{16}b} [1]{tca.stored_output(1):0{16}b}")
print(f"True Out = [0]{tca.read_output(0):0{16}b} [1]{tca.read_output(1):0{16}b}")
print("-----------")
print("\n")

try:
#print_tca()

# Create a Quad Servo Direct class for each populated module slot
for slot in yukon.find_slots_with_module(BigMotorModule):
big_motor = BigMotorModule()
yukon.register_with_slot(big_motor, slot)
motor_modules.append(big_motor)

# Initialise Yukon's registered modules
yukon.initialise_modules(allow_unregistered=True)

NUM_MOTORS = len(motor_modules)
print(f"Up to {NUM_MOTORS} motors available")

#time.sleep(5)

# Turn on the module power
yukon.enable_main_output()

#print_tca()

# Enable the outputs on the regulated servo modules
for module in motor_modules:
try:
module.enable()
except AttributeError:
# No enable function
pass

#print_tca("post_enable_motors")

offset = 0
while not yukon.is_boot_pressed():
offset += SPEED / 1000.0

# Update all the Motors
i = 0
for module in motor_modules:
angle = ((i / NUM_MOTORS) + offset) * math.pi * 2
module.motor.speed(math.sin(angle) * MOTOR_EXTENT)
i += 1

#yukon.monitored_sleep(1.0 / UPDATES, allowed=("T_avg", "Fault"))
yukon.monitored_sleep(1.0, allowed=("T_avg", "Fault"))

finally:
# Put the board back into a safe state, regardless of how the program may have ended
yukon.reset()
57 changes: 57 additions & 0 deletions examples/yukon_adaptive_motor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import time
import math
from pimoroni_yukon import Yukon
from pimoroni_yukon.modules import DualMotorModule

SPEED = 5 # The speed that the motors will cycle at
UPDATES = 50 # How many times to update LEDs and Servos per second
MOTOR_EXTENT = 1.0 # How far from zero to drive the motors

# Create a Yukon object to begin using the board
yukon = Yukon()

# List to store the modules
motor_modules = []

try:
# Create a Quad Servo Direct class for each populated module slot
for slot in yukon.find_slots_with_module(DualMotorModule):
dual_motor = DualMotorModule()
yukon.register_with_slot(dual_motor, slot)
motor_modules.append(dual_motor)

# Initialise Yukon's registered modules
yukon.initialise_modules(allow_unregistered=True)

NUM_MOTORS = len(motor_modules) * DualMotorModule.NUM_MOTORS
print(f"Up to {NUM_MOTORS} motors available")

# Turn on the module power
yukon.enable_main_output()

# Enable the outputs on the regulated servo modules
for module in motor_modules:
try:
module.enable()
except AttributeError:
# No enable function
pass

offset = 0
while not yukon.is_boot_pressed():
offset += SPEED / 1000.0

# Update all the Motors
i = 0
for module in motor_modules:
for motor in module.motors:
angle = ((i / NUM_MOTORS) + offset) * math.pi * 2
motor.speed(math.sin(angle) * MOTOR_EXTENT)
i += 1

yukon.monitored_sleep(1.0 / UPDATES)

finally:
# Put the board back into a safe state, regardless of how the program may have ended
yukon.reset()

62 changes: 62 additions & 0 deletions examples/yukon_adaptive_servo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import time
import math
from pimoroni_yukon import Yukon
from pimoroni_yukon.modules import QuadServoDirectModule, QuadServoRegModule

SPEED = 5 # The speed that the servos will cycle at
UPDATES = 50 # How many times to update LEDs and Servos per second
SERVO_EXTENT = 80.0 # How far from zero to move the servos

# Create a Yukon object to begin using the board
yukon = Yukon(logging_level=2)

# List to store the modules
servo_modules = []

try:
# Create a Quad Servo Direct class for each populated module slot
for slot in yukon.find_slots_with_module(QuadServoDirectModule):
direct_servo = QuadServoDirectModule()
yukon.register_with_slot(direct_servo, slot)
servo_modules.append(direct_servo)

# Create a Quad Servo Reg class for each populated module slot
for slot in yukon.find_slots_with_module(QuadServoRegModule):
reg_servo = QuadServoRegModule()
yukon.register_with_slot(reg_servo, slot)
servo_modules.append(reg_servo)

# Initialise Yukon's registered modules
yukon.initialise_modules(allow_unregistered=True)

NUM_SERVOS = len(servo_modules) * QuadServoDirectModule.NUM_SERVOS
print(f"Up to {NUM_SERVOS} servos available")

# Turn on the module power
yukon.enable_main_output()

# Enable the outputs on the regulated servo modules
for module in servo_modules:
try:
module.enable()
except AttributeError:
# No enable function
pass

offset = 0
while not yukon.is_boot_pressed():
offset += SPEED / 1000.0

# Update all the Servos
i = 0
for module in servo_modules:
for servo in module.servos:
angle = ((i / NUM_SERVOS) + offset) * math.pi * 2
servo.value(math.sin(angle) * SERVO_EXTENT)
i += 1

yukon.monitored_sleep(1.0 / UPDATES)

finally:
# Put the board back into a safe state, regardless of how the program may have ended
yukon.reset()
26 changes: 13 additions & 13 deletions firmware/filesystem/lib/pimoroni_yukon/modules/big_motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,42 +32,42 @@ def __init__(self, frequency=DEFAULT_FREQUENCY):
def initialise(self, slot, adc1_func, adc2_func):
try:
# Create pwm objects
self.__pwm_p = PWMOut(slot.FAST4, frequency=self.__frequency)
self.__pwm_n = PWMOut(slot.FAST3, frequency=self.__frequency)
self.__pwm_p = slot.FAST4
self.__pwm_n = slot.FAST3
except ValueError as e:
if slot.ID <= 2 or slot.ID >= 5:
conflicting_slot = (((slot.ID - 1) + 4) % 8) + 1
raise type(e)(f"PWM channel(s) already in use. Check that the module in Slot{conflicting_slot} does not share the same PWM channel(s)") from None
raise type(e)("PWM channel(s) already in use. Check that a module in another slot does not share the same PWM channel(s)") from None

# Create motor object
self.motor = DCMotor(self.__pwm_p, self.__pwm_n)
self.motor = Motor((self.__pwm_p, self.__pwm_n), freq=self.__frequency)

# Create motor control pin objects
self.__motor_en = DigitalInOut(slot.SLOW3)
self.__motor_nfault = DigitalInOut(slot.SLOW2)
self.__motor_en = slot.SLOW3
self.__motor_nfault = slot.SLOW2

# Pass the slot and adc functions up to the parent now that module specific initialisation has finished
super().initialise(slot, adc1_func, adc2_func)

def configure(self):
self.motor.throttle = None
self.motor.decay_mode = SLOW_DECAY
self.motor.disable()
self.motor.decay_mode(SLOW_DECAY)

self.__motor_nfault.switch_to_input()
self.__motor_en.switch_to_output(False)
self.__motor_nfault.init(Pin.IN)
self.__motor_en.init(Pin.OUT, value=False)

def enable(self):
self.__motor_en.value = True
self.__motor_en.value(True)

def disable(self):
self.__motor_en.value = False
self.__motor_en.value(False)

def is_enabled(self):
return self.__motor_en.value
return self.__motor_en.value() == 1

def read_fault(self):
return not self.__motor_nfault.value
return self.__motor_nfault.value() != 1

def read_current(self):
# This needs more validation
Expand Down
38 changes: 18 additions & 20 deletions firmware/filesystem/lib/pimoroni_yukon/modules/dual_motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,65 +36,63 @@ def __init__(self, motor_type=DUAL, frequency=DEFAULT_FREQUENCY):
def initialise(self, slot, adc1_func, adc2_func):
try:
# Create pwm objects
self.__pwms_p = [PWMOut(slot.FAST2, frequency=self.__frequency),
PWMOut(slot.FAST4, frequency=self.__frequency)]
self.__pwms_n = [PWMOut(slot.FAST1, frequency=self.__frequency),
PWMOut(slot.FAST3, frequency=self.__frequency)]
self.__pwms_p = (slot.FAST2, slot.FAST4)
self.__pwms_n = (slot.FAST1, slot.FAST3)
except ValueError as e:
if slot.ID <= 2 or slot.ID >= 5:
conflicting_slot = (((slot.ID - 1) + 4) % 8) + 1
raise type(e)(f"PWM channel(s) already in use. Check that the module in Slot{conflicting_slot} does not share the same PWM channel(s)") from None
raise type(e)("PWM channel(s) already in use. Check that a module in another slot does not share the same PWM channel(s)") from None

if self.__motor_type == self.DUAL:
from adafruit_motor.motor import DCMotor
from motor import Motor

# Create motor objects
self.motors = [DCMotor(self.__pwms_p[i], self.__pwms_n[i]) for i in range(len(self.__pwms_p))]
self.motors = [Motor((self.__pwms_p[i], self.__pwms_n[i]), freq=self.__frequency) for i in range(len(self.__pwms_p))]
else:
from adafruit_motor.stepper import StepperMotor

self.stepper = StepperMotor(self.__pwms_p[0], self.__pwms_n[0], self.__pwms_p[1], self.__pwms_n[1])

# Create motor control pin objects
self.__motors_decay = DigitalInOut(slot.SLOW1)
self.__motors_toff = DigitalInOut(slot.SLOW2)
self.__motors_en = DigitalInOut(slot.SLOW3)
self.__motors_decay = slot.SLOW1
self.__motors_toff = slot.SLOW2
self.__motors_en = slot.SLOW3

# Pass the slot and adc functions up to the parent now that module specific initialisation has finished
super().initialise(slot, adc1_func, adc2_func)

def configure(self):
if self.__motor_type == self.DUAL:
for motor in self.motors:
motor.throttle = None
motor.disable()
else:
self.stepper.release()

self.__motors_decay.switch_to_output(False)
self.__motors_toff.switch_to_output(False)
self.__motors_en.switch_to_output(False)
self.__motors_decay.init(Pin.OUT, value=False)
self.__motors_toff.init(Pin.OUT, value=False)
self.__motors_en.init(Pin.OUT, value=False)

def enable(self):
self.__motors_en.value = True
self.__motors_en.value(True)

def disable(self):
self.__motors_en.value = False
self.__motors_en.value(False)

def is_enabled(self):
return self.__motors_en.value
return self.__motors_en.value() == 1

def decay(self, value=None):
if value is None:
return self.__motors_decay
return self.__motors_decay() == 1
else:
self.__motors_decay = value
self.__motors_decay(value)

def toff(self, value=None):
if value is None:
return self.__motors_toff
return self.__motors_toff() == 1
else:
self.__motors_toff = value
self.__motors_toff(value)

@property
def motor1(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,25 @@ def __init__(self):
def initialise(self, slot, adc1_func, adc2_func):
try:
# Create pwm objects
self.__pwms = [PWMOut(slot.FAST1, frequency=50),
PWMOut(slot.FAST2, frequency=50),
PWMOut(slot.FAST3, frequency=50),
PWMOut(slot.FAST4, frequency=50)]
self.__pwms = (slot.FAST1,
slot.FAST2,
slot.FAST3,
slot.FAST4)
except ValueError as e:
if slot.ID <= 2 or slot.ID >= 5:
conflicting_slot = (((slot.ID - 1) + 4) % 8) + 1
raise type(e)(f"PWM channel(s) already in use. Check that the module in Slot{conflicting_slot} does not share the same PWM channel(s)") from None
raise type(e)("PWM channel(s) already in use. Check that a module in another slot does not share the same PWM channel(s)") from None

# Create servo objects
self.servos = [Servo(self.__pwms[i]) for i in range(len(self.__pwms))]
self.servos = [Servo(self.__pwms[i], freq=50) for i in range(len(self.__pwms))]

# Pass the slot and adc functions up to the parent now that module specific initialisation has finished
super().initialise(slot, adc1_func, adc2_func)

def configure(self):
for servo in self.servos:
servo.angle = None
servo.disable()

@property
def servo1(self):
Expand Down
Loading

0 comments on commit bf1f6e5

Please sign in to comment.