Add adafruit servokit compatible pololu library
This commit is contained in:
		
							
								
								
									
										190
									
								
								software/raspberry pi/adafruit_servokit.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								software/raspberry pi/adafruit_servokit.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
				
			|||||||
 | 
					# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# MODIFIED to use pololu motor drivers, but compatible with adafruit_servokit driver layout
 | 
				
			||||||
 | 
					# by Cole Deck
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					`adafruit_servokit`
 | 
				
			||||||
 | 
					====================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CircuitPython helper library for the PWM/Servo FeatherWing, Shield and Pi HAT and Bonnet kits.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Author(s): Kattni Rembor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Implementation Notes
 | 
				
			||||||
 | 
					--------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Hardware:**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `8-Channel PWM or Servo FeatherWing <https://www.adafruit.com/product/2928>`_
 | 
				
			||||||
 | 
					* `Adafruit 16-Channel 12-bit PWM/Servo Shield <https://www.adafruit.com/product/1411>`_
 | 
				
			||||||
 | 
					* `Adafruit 16-Channel PWM/Servo HAT for Raspberry Pi <https://www.adafruit.com/product/2327>`_
 | 
				
			||||||
 | 
					* `Adafruit 16-Channel PWM/Servo Bonnet for Raspberry Pi <https://www.adafruit.com/product/3416>`_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Software and Dependencies:**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Adafruit CircuitPython firmware for the supported boards:
 | 
				
			||||||
 | 
					  https://github.com/adafruit/circuitpython/releases
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 | 
				
			||||||
 | 
					* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
 | 
				
			||||||
 | 
					* Adafruit's PCA9685 library: https://github.com/adafruit/Adafruit_CircuitPython_PCA9685
 | 
				
			||||||
 | 
					* Adafruit's Motor library: https://github.com/adafruit/Adafruit_CircuitPython_Motor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#import board
 | 
				
			||||||
 | 
					#from adafruit_pca9685 import PCA9685
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    from typing import Optional
 | 
				
			||||||
 | 
					    import maestro
 | 
				
			||||||
 | 
					    #from busio import I2C
 | 
				
			||||||
 | 
					    #from adafruit_motor.servo import Servo, ContinuousServo
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__version__ = "0.0.0+auto.0"
 | 
				
			||||||
 | 
					__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ServoKit.git"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pca = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServoKit:
 | 
				
			||||||
 | 
					    """Class representing an Adafruit PWM/Servo FeatherWing, Shield or Pi HAT and Bonnet kits.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Automatically uses the I2C bus on a Feather, Metro or Raspberry Pi.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Initialise the PCA9685 chip at ``address``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The internal reference clock is 25MHz but may vary slightly with environmental conditions and
 | 
				
			||||||
 | 
					    manufacturing variances. Providing a more precise ``reference_clock_speed`` can improve the
 | 
				
			||||||
 | 
					    accuracy of the frequency and duty_cycle computations. See the ``calibration.py`` example in
 | 
				
			||||||
 | 
					    the `PCA9685 GitHub repo <https://github.com/adafruit/Adafruit_CircuitPython_PCA9685>`_ for
 | 
				
			||||||
 | 
					    how to derive this value by measuring the resulting pulse widths.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param int channels: The number of servo channels available. Must be 8 or 16. The FeatherWing
 | 
				
			||||||
 | 
					                         has 8 channels. The Shield, HAT, and Bonnet have 16 channels.
 | 
				
			||||||
 | 
					    :param ~I2C i2c: The I2C bus to use. If not provided, it will use generate the default I2C
 | 
				
			||||||
 | 
					                                      bus singleton ``busio.I2C()`` and use that.
 | 
				
			||||||
 | 
					    :param int address: The I2C address of the PCA9685. Default address is ``0x40``.
 | 
				
			||||||
 | 
					    :param int reference_clock_speed: The frequency of the internal reference clock in Hertz.
 | 
				
			||||||
 | 
					                                      Default reference clock speed is ``25000000``.
 | 
				
			||||||
 | 
					    :param int frequency: The overall PWM frequency of the PCA9685 in Hertz.
 | 
				
			||||||
 | 
					                                      Default frequency is ``50``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        *,
 | 
				
			||||||
 | 
					        channels: int,
 | 
				
			||||||
 | 
					        #i2c: Optional[I2C] = None,
 | 
				
			||||||
 | 
					        address: str = "/dev/ttyACM0",
 | 
				
			||||||
 | 
					        reference_clock_speed: int = 25000000,
 | 
				
			||||||
 | 
					        frequency: int = 50 # actually, device ID over serial pololu protocol
 | 
				
			||||||
 | 
					    ) -> None:
 | 
				
			||||||
 | 
					        if channels not in [6, 12, 18, 24]:
 | 
				
			||||||
 | 
					            raise ValueError("servo_channels must be 6, 12, 18, 24!")
 | 
				
			||||||
 | 
					        self._items = [None] * channels
 | 
				
			||||||
 | 
					        self._channels = channels
 | 
				
			||||||
 | 
					        #if i2c is None:
 | 
				
			||||||
 | 
					        #    i2c = board.I2C()
 | 
				
			||||||
 | 
					        #self._pca = PCA9685(
 | 
				
			||||||
 | 
					        #    i2c, address=address, reference_clock_speed=reference_clock_speed
 | 
				
			||||||
 | 
					        #)
 | 
				
			||||||
 | 
					        #self._pca.frequency = frequency
 | 
				
			||||||
 | 
					        global pca
 | 
				
			||||||
 | 
					        self._pca = maestro.Controller(ttyStr=address,device=frequency)
 | 
				
			||||||
 | 
					        pca = self._pca
 | 
				
			||||||
 | 
					        self._servo = _Servo(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def servo(self) -> "_Servo":
 | 
				
			||||||
 | 
					        """:class:`~adafruit_motor.servo.Servo` controls for standard servos.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This FeatherWing example rotates a servo on channel ``0`` to ``180`` degrees for one second,
 | 
				
			||||||
 | 
					        and then returns it to ``0`` degrees.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. code-block:: python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            import time
 | 
				
			||||||
 | 
					            from adafruit_servokit import ServoKit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            kit = ServoKit(channels=8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            kit.servo[0].angle = 180
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					            kit.servo[0].angle = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self._servo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _Servo:
 | 
				
			||||||
 | 
					    # pylint: disable=protected-access
 | 
				
			||||||
 | 
					    def __init__(self, kit: ServoKit) -> None:
 | 
				
			||||||
 | 
					        self.kit = kit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getitem__(self, servo_channel: int):
 | 
				
			||||||
 | 
					        #import adafruit_motor.servo  # pylint: disable=import-outside-toplevel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        num_channels = self.kit._channels
 | 
				
			||||||
 | 
					        if servo_channel >= num_channels or servo_channel < 0:
 | 
				
			||||||
 | 
					            raise ValueError("servo must be 0-{}!".format(num_channels - 1))
 | 
				
			||||||
 | 
					        servo = pololuservo(servo_channel)
 | 
				
			||||||
 | 
					        return servo
 | 
				
			||||||
 | 
					        raise ValueError("Channel {} is already in use.".format(servo_channel))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __len__(self) -> int:
 | 
				
			||||||
 | 
					        return len(self.kit._items)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class pololuservo:
 | 
				
			||||||
 | 
					    def __init__(self, ch, actuation_range: int = 180, min_pulse: int = 3000, max_pulse: int = 9000):
 | 
				
			||||||
 | 
					        self.ch = ch
 | 
				
			||||||
 | 
					        self.actuation_range = actuation_range
 | 
				
			||||||
 | 
					        self._min_duty = min_pulse
 | 
				
			||||||
 | 
					        self._max_duty = max_pulse
 | 
				
			||||||
 | 
					        self._duty_range = max_pulse = min_pulse
 | 
				
			||||||
 | 
					        self.duty_cycle = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def angle(self) -> Optional[float]:
 | 
				
			||||||
 | 
					        """The servo angle in degrees. Must be in the range ``0`` to ``actuation_range``.
 | 
				
			||||||
 | 
					        Is None when servo is disabled."""
 | 
				
			||||||
 | 
					        if self.fraction is None:  # special case for disabled servos
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        return self.actuation_range * self.fraction
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @angle.setter
 | 
				
			||||||
 | 
					    def angle(self, new_angle: Optional[int]) -> None:
 | 
				
			||||||
 | 
					        if new_angle is None:  # disable the servo by sending 0 signal
 | 
				
			||||||
 | 
					            self.fraction = None
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        if new_angle < 0 or new_angle > self.actuation_range:
 | 
				
			||||||
 | 
					            raise ValueError("Angle out of range")
 | 
				
			||||||
 | 
					        self.fraction = new_angle / self.actuation_range
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def fraction(self) -> Optional[float]:
 | 
				
			||||||
 | 
					        """Pulse width expressed as fraction between 0.0 (`min_pulse`) and 1.0 (`max_pulse`).
 | 
				
			||||||
 | 
					        For conventional servos, corresponds to the servo position as a fraction
 | 
				
			||||||
 | 
					        of the actuation range. Is None when servo is diabled (pulsewidth of 0ms).
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self.duty_cycle == 0:  # Special case for disabled servos
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        return (self.duty_cycle - self._min_duty) / self._duty_range
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @fraction.setter
 | 
				
			||||||
 | 
					    def fraction(self, value: Optional[float]) -> None:
 | 
				
			||||||
 | 
					        if value is None:
 | 
				
			||||||
 | 
					            #self._pwm_out.duty_cycle = 0  # disable the motor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        if not 0.0 <= value <= 1.0:
 | 
				
			||||||
 | 
					            raise ValueError("Must be 0.0 to 1.0")
 | 
				
			||||||
 | 
					        self.duty_cycle = self._min_duty + int(value * self._duty_range)
 | 
				
			||||||
 | 
					        #print(self.ch)
 | 
				
			||||||
 | 
					        pca.setTarget(self.ch,self.duty_cycle)
 | 
				
			||||||
 | 
					        #self._pwm_out.duty_cycle = duty_cycle
 | 
				
			||||||
@@ -49,28 +49,32 @@ class BluetoothServer(Thread):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.cmd_queue = out_cmd_queue
 | 
					        self.cmd_queue = out_cmd_queue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with open('/home/pi/hexapod/software/raspberry pi/config.json', 'r') as read_file:
 | 
					        with open('/home/cole/Downloads/hexapod/software/raspberry pi/config.json', 'r') as read_file:
 | 
				
			||||||
            self.config = json.load(read_file)
 | 
					            self.config = json.load(read_file)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        stream = os.popen('hciconfig hci0')
 | 
					            stream = os.popen('hciconfig hci0')
 | 
				
			||||||
        output = stream.read()
 | 
					            output = stream.read()
 | 
				
			||||||
        device_id = "hci0"
 | 
					            device_id = "hci0"
 | 
				
			||||||
        bt_mac = output.split("{}:".format(device_id))[1].split(
 | 
					            bt_mac = output.split("{}:".format(device_id))[1].split(
 | 
				
			||||||
            "BD Address: ")[1].split(" ")[0].strip()
 | 
					                "BD Address: ")[1].split(" ")[0].strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.mac = bt_mac
 | 
					            self.mac = bt_mac
 | 
				
			||||||
        self.port = 10
 | 
					            self.port = 10
 | 
				
			||||||
        self.bt_socket = socket.socket(
 | 
					            self.bt_socket = socket.socket(
 | 
				
			||||||
            socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
 | 
					                socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.signal = self.SIG_NORMAL
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        self.signal = self.SIG_NORMAL
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self):
 | 
					    def run(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.bt_socket.bind((self.mac, self.port))
 | 
					            self.bt_socket.bind((self.mac, self.port))
 | 
				
			||||||
            self.bt_socket.listen(1)
 | 
					            self.bt_socket.listen(1)
 | 
				
			||||||
            print('TCP listening')
 | 
					            print('TCP listening')
 | 
				
			||||||
        except OSError as err:
 | 
					        except: # OSError as err:
 | 
				
			||||||
            # print('emit tcp server error')
 | 
					            # print('emit tcp server error')
 | 
				
			||||||
            # self.status.emit(self.STOP, '')
 | 
					            # self.status.emit(self.STOP, '')
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
@@ -104,6 +108,9 @@ class BluetoothServer(Thread):
 | 
				
			|||||||
                                break
 | 
					                                break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            self.bt_socket.close()
 | 
					            try:
 | 
				
			||||||
            self.cmd_queue.put('standby:')
 | 
					                self.bt_socket.close()
 | 
				
			||||||
            print('exit')
 | 
					                self.cmd_queue.put('standby:')
 | 
				
			||||||
 | 
					                print('exit')
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,7 @@
 | 
				
			|||||||
# https://circuitpython.readthedocs.io/projects/servokit/en/latest/
 | 
					# https://circuitpython.readthedocs.io/projects/servokit/en/latest/
 | 
				
			||||||
from audioop import reverse
 | 
					from audioop import reverse
 | 
				
			||||||
from adafruit_servokit import ServoKit
 | 
					from adafruit_servokit import ServoKit
 | 
				
			||||||
 | 
					import maestro
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from leg import Leg
 | 
					from leg import Leg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -92,7 +93,7 @@ class Hexapod(Thread):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.calibration_mode = False
 | 
					        self.calibration_mode = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with open('/home/pi/hexapod/software/raspberry pi/config.json', 'r') as read_file:
 | 
					        with open('/home/cole/Downloads/hexapod/software/raspberry pi/config.json', 'r') as read_file:
 | 
				
			||||||
            self.config = json.load(read_file)
 | 
					            self.config = json.load(read_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # legs' coordinates
 | 
					        # legs' coordinates
 | 
				
			||||||
@@ -115,8 +116,8 @@ class Hexapod(Thread):
 | 
				
			|||||||
        self.mount_position[:, 1] = self.mount_y
 | 
					        self.mount_position[:, 1] = self.mount_y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Objects
 | 
					        # Objects
 | 
				
			||||||
        self.pca_right = ServoKit(channels=16, address=0x40, frequency=120)
 | 
					        self.pca_right = ServoKit(channels=18, address="/dev/ttyUSB0", frequency=0x0c)
 | 
				
			||||||
        self.pca_left = ServoKit(channels=16, address=0x41, frequency=120)
 | 
					        self.pca_left = ServoKit(channels=18, address="/dev/ttyUSB0", frequency=0x0d)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.legs = [
 | 
					        self.legs = [
 | 
				
			||||||
            # front right
 | 
					            # front right
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1,2 @@
 | 
				
			|||||||
pyserial
 | 
					pyserial
 | 
				
			||||||
 | 
					numpy
 | 
				
			||||||
@@ -47,7 +47,7 @@ class TCPServer(Thread):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.cmd_queue = out_cmd_queue
 | 
					        self.cmd_queue = out_cmd_queue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with open('/home/pi/hexapod/software/raspberry pi/config.json', 'r') as read_file:
 | 
					        with open('/home/cole/Downloads/hexapod/software/raspberry pi/config.json', 'r') as read_file:
 | 
				
			||||||
            self.config = json.load(read_file)
 | 
					            self.config = json.load(read_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.ip = '192.168.1.127'
 | 
					        self.ip = '192.168.1.127'
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user