Giuseppe Parrello

 

Embedded Servo Motor SG90


Introduction

With this project we will manage a Servo Motor TowerPro SG90, using the FTDI FT232H development board (of which there is a dedicated page on this site) and the NXP PCA9685 development board (of which there is a dedicated page on this site).

 

Connection

This Servo Motor must be connected to the NXP PCA9685 development board. The connection connectors are listed below:

Image Board FT232H Board PCA9685 Servo Motor

FT232H - SG90

AD0 SCL ------
AD1 + AD2 SDA ------
+5V VCC ------
GND GND ------
------ Canale 0 - PWM PWM
+5V ------ VCC
------ Canale 0 - GND GND

 

Python code

To manage this Servo Motor, the presence of the "PCA9685_FTDI" library is required.
The following Python code example rotates the Servo Motor using a "Throttle" command and then an "Angle" command to rotate the motor:

# Simple demo of of the PCA9685 PWM servo/LED controller library.
import time

# Import the PCA9685 module.
import pca9685_ftdi

# Initialise the PCA9685 using the default address (0x40).
pwm = pca9685_ftdi.PCA9685_FTDI()

SERVO_FREQ = 50    # Analog servos run at ~50 Hz updates
pwm.setPWMFreq(SERVO_FREQ)  # Analog servos run at ~50 Hz updates

# our servo # counter
servonum = 0

actuation_range = 180
min_pulse       = 750
max_pulse       = 2250
_min_duty       = int((min_pulse * SERVO_FREQ) / 1000000 * 0xFFFF)
max_duty        = (max_pulse * SERVO_FREQ) / 1000000 * 0xFFFF
_duty_range     = int(max_duty - _min_duty)

def set_duty_cycle(value):
    if not 0 <= value <= 0xFFFF:
        raise ValueError("Out of range")
    if value == 0xFFFF:
        #print("set_duty_cycle", value)
        pwm.set_pwm(servonum, 0x1000, 0)
    else:
        value = (value + 1) >> 4
        #print("set_duty_cycle", value)
        pwm.set_pwm(servonum, 0, value)

def set_fraction(value):
    if value is None:
        set_duty_cycle(0)
        return
    if not 0.0 <= value <= 1.0:
        raise ValueError("Must be 0.0 to 1.0")
    duty_cycle = _min_duty + int(value * _duty_range)
    #print("duty_cycle", duty_cycle)
    set_duty_cycle(duty_cycle)

def angle(new_angle):
    if new_angle < 0 or new_angle > actuation_range:
        raise ValueError("Angle out of range")
    set_fraction(new_angle / actuation_range)

def throttle(value):
    if value > 1.0 or value < -1.0:
        raise ValueError("Throttle must be between -1.0 and 1.0")
    fraction = (value + 1) / 2
    set_fraction(fraction)

try:
    print("Throttle 1")
    throttle(1)
    time.sleep(2)
    print("Throttle -1")
    throttle(-1)
    time.sleep(2)
    print("Throttle 0\n")
    throttle(0)
    time.sleep(2)

    print("Angle 0")
    angle(0)
    time.sleep(2)
    print("Angle 90")
    angle(90)
    time.sleep(2)
    print("Angle 180")
    angle(180)
    time.sleep(2)
    print("Angle 0")
    angle(0)

except KeyboardInterrupt:
    # Capture keyboard ^C to exit the program
    print('\nYou terminated the program. The program ends!')
    angle(0)
    pwm.set_all_pwm(0, 0)
    pwm.close()