Skip to content

Commit

Permalink
Support for serial servo module
Browse files Browse the repository at this point in the history
  • Loading branch information
ZodiusInfuser committed Sep 29, 2023
1 parent 2e43acd commit 5c37784
Show file tree
Hide file tree
Showing 4 changed files with 520 additions and 1 deletion.
77 changes: 77 additions & 0 deletions examples/yukon_adaptive_serial_servo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import time
import math
from pimoroni_yukon import Yukon
from pimoroni_yukon.modules import SerialServoModule
from pimoroni_yukon.protocols.lx_servos import *

SPEED = 5 # The speed that the servos will cycle at
UPDATES = 5 # 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=1)

# 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(SerialServoModule):
serial_servo = SerialServoModule()
yukon.register_with_slot(serial_servo, slot)
servo_modules.append(serial_servo)

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

# Turn on the module power
yukon.enable_main_output()

time.sleep(1)

old_id = 1
new_id = 11

servo_ids = [1, 2, 3]

#for module in servo_modules:
# module.send_on_data()
# SerialServoSetID(module.uart, old_id, new_id)

# for module in servo_modules:
# module.send_on_data()
# SerialServoSetMode(module.uart, 2, 1, 1000)

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

# Update all the Servos
for module in servo_modules:
for sid in servo_ids:
angle = SerialServoReadPosition(module.uart, module.send_on_data, module.receive_on_data, sid)
angle = ((angle - 500) / 360) * 90
voltage = SerialServoReadVin(module.uart, module.send_on_data, module.receive_on_data, sid)
voltage = (voltage / 1000)
temp = SerialServoReadTemperature(module.uart, module.send_on_data, module.receive_on_data, sid)
print(f"ID: {sid}, Angle: {angle}, Vin: {voltage}, Temp: {temp}", end=", ")
print()

if yukon.is_pressed('A'):
SerialServoMove(module.uart, 1, 500, 1000)

if yukon.is_pressed('B'):
SerialServoMove(module.uart, 1, 800, 1000)

toggle = not toggle
if toggle:
SerialServoActivateLED(module.uart, 1)
else:
SerialServoDeactivateLED(module.uart, 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()
4 changes: 3 additions & 1 deletion lib/pimoroni_yukon/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .proto import ProtoPotModule
from .quad_servo_direct import QuadServoDirectModule
from .quad_servo_reg import QuadServoRegModule
from .serial_servo import SerialServoModule


KNOWN_MODULES = (
Expand All @@ -22,5 +23,6 @@
LEDStripModule,
ProtoPotModule,
QuadServoDirectModule,
QuadServoRegModule
QuadServoRegModule,
SerialServoModule
)
91 changes: 91 additions & 0 deletions lib/pimoroni_yukon/modules/serial_servo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
#
# SPDX-License-Identifier: MIT

from .common import YukonModule, ADC_FLOAT, LOW, HIGH
from busio import UART
from digitalio import DigitalInOut
from collections import OrderedDict
from pimoroni_yukon.errors import OverTemperatureError


class SerialServoModule(YukonModule):
NAME = "Serial Bus Servo"
DEFAULT_BAUDRATE = 115200
TEMPERATURE_THRESHOLD = 50.0

# | ADC1 | SLOW1 | SLOW2 | SLOW3 | Module | Condition (if any) |
# |-------|-------|-------|-------|----------------------|-----------------------------|
# | FLOAT | 1 | 0 | 0 | Serial Servo | |
@staticmethod
def is_module(adc_level, slow1, slow2, slow3):
return adc_level is ADC_FLOAT and slow1 is HIGH and slow2 is LOW and slow3 is LOW

def __init__(self, baudrate=DEFAULT_BAUDRATE):
super().__init__()

self.__baudrate = baudrate

def initialise(self, slot, adc1_func, adc2_func):
try:
# Create the serial object
self.uart = UART(slot.FAST1, slot.FAST2, baudrate=self.__baudrate)
except ValueError as e:
raise type(e)("UART perhiperal already in use. Check that a module in another slot does not share the same UART perhiperal") from None

# Create the direction pin objects
self.__tx_to_data_en = DigitalInOut(slot.FAST3)
self.__data_to_rx_en = DigitalInOut(slot.FAST4)

# 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 reset(self):
self.uart.reset_input_buffer()

self.__tx_to_data_en.switch_to_output(True) # Active low
self.__data_to_rx_en.switch_to_output(True) # Active low

def send_on_data(self):
self.__data_to_rx_en.value = True
self.__tx_to_data_en.value = False

def receive_on_data(self):
self.__tx_to_data_en.value = True
self.__data_to_rx_en.value = False

def read_temperature(self):
return self.__read_adc2_as_temp()

def monitor(self):
temperature = self.read_temperature()
if temperature > self.TEMPERATURE_THRESHOLD:
raise OverTemperatureError(self.__message_header() + f"Temperature of {temperature}°C exceeded the limit of {self.TEMPERATURE_THRESHOLD}°C! Turning off output")

# Run some user action based on the latest readings
if self.__monitor_action_callback is not None:
self.__monitor_action_callback(temperature)

self.__max_temperature = max(temperature, self.__max_temperature)
self.__min_temperature = min(temperature, self.__min_temperature)
self.__avg_temperature += temperature

self.__count_avg += 1

def get_readings(self):
return OrderedDict({
"T_max": self.__max_temperature,
"T_min": self.__min_temperature,
"T_avg": self.__avg_temperature
})

def process_readings(self):
if self.__count_avg > 0:
self.__avg_temperature /= self.__count_avg

def clear_readings(self):
self.__max_temperature = float('-inf')
self.__min_temperature = float('inf')
self.__avg_temperature = 0

self.__count_avg = 0
Loading

0 comments on commit 5c37784

Please sign in to comment.