servo

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2020 License: MIT Imports: 9 Imported by: 1

README

Servo

Version PkgGoDev License Go version

Servo is a Go library to control servo motors on a Raspberry Pi using pi-blaster.

DISCLAIMER

This library controls physical pins of a Raspberry Pi using pi-blaster. Incorrect pin assignment or pi-blaster configuration may DAMAGE the Raspberry Pi or connected devices. Please, make sure to carefully check your connections and code before running your program.

You have been warned.

Good luck and Godspeed!

About the library

This library uses pi-blaster to control servo motors on a Raspberry Pi. Under the hood, it opens a pipeline to /dev/pi-blaster and sends commands in the format GPIO=PWM. The library calculates the appropriate PWM based on the speed and position of the servo and groups the writes to /dev/pi-blaster at a rate of 40 ms, if multiple servos are connected. You can check the documentation for more detailed information.

Each connected servo is managed independently from one another and is designed to be concurrent-safe.

If the package servo detects that pi-blaster is not running on the system when executed, it will throw a warning:

YYYY/MM/DD HH:mm:ss WARNING: pi-blaster was not found running: start pi-blaster to avoid this error
        (servo will continue with pi-blaster disabled)

and redirect all writes to /dev/null. This way, you can build and test your code on machines other than a Raspberry Pi or do a cold run before committing.

Testing your System

To check if your system can handle real-time control of servos (i.e. move the servos at the expected speed), a system check and a stress test are provided.

You can run them with:

$ cd $(go env GOPATH)/src/github.com/cgxeiji/servo
$ go test -v

This test makes sure that the system is capable of running 100 servos concurrently within the expected time frame. It simulates connecting 100 servos and moving them from 0 to 180 degrees, from 180 to 0 degrees, and from 0 to 180 degrees for a total of 3 passes. At the nominal speed of a common TowerPro servo of 0.19s/60degrees, it should take approximately 570ms +/- 50ms for the servo to move 180 degrees.

For benchmarking, the test also checks 1,000 and 10,000 concurrent servos connected, but these tests won't throw critical failures.

In simple terms, if your Raspberry Pi is capable of running 100 servos at the same time (which is a number way above the number of pins available), you can be confident that the servos will be controlled as expected.

Example code

package main

import (
	"fmt"
	"log"

	"github.com/cgxeiji/servo"
)

func main() {
	// Use servo.Close() to close the connection of all servos and pi-blaster.
	defer servo.Close()

	// If you want to move the servos, make sure that pi-blaster is running.
	// For example, start pi-blaster as:
	// $ sudo pi-blaster --gpio 14 --pcm

	// Create a new servo connected to gpio 14.
	myServo := servo.New(14)
	// (optional) Initialize the servo with your preferred values.
	// myServo.Flags = servo.Normalized | servo.Centered
	myServo.MinPulse = 0.05 // Set the minimum pwm pulse width (default: 0.05).
	myServo.MaxPulse = 0.25 // Set the maximum pwm pulse width (default: 0.25).
	myServo.SetPosition(90) // Set the initial position to 90 degrees.
	myServo.SetSpeed(0.2)   // Set the speed to 20% (default: 1.0).
	// NOTE: The maximum speed of the servo is 0.19s/60degrees.
	// (optional) Set a verbose name.
	myServo.Name = "My Servo"

	// Print the information of the servo.
	fmt.Println(myServo)

	// Connect the servo to the daemon.
	err := myServo.Connect()
	if err != nil {
		log.Fatal(err)
	}

	// (optional) Use myServo.Close() to close the connection to a specific
	// servo. You still need to close the connection to pi-blaster with
	// `servo.Close()`.
	defer myServo.Close()

	myServo.SetSpeed(0.5) // Set the speed to half. This is concurrent-safe.
	myServo.MoveTo(180)   // This is a non-blocking call.

	/* do some work */

	myServo.Wait() // Call Wait() to sync with the servo.

	// MoveTo() returns a Waiter interface that can be used to move and wait on
	// the same line.
	myServo.MoveTo(0).Wait() // This is a blocking call.
}

Documentation

Overview

Example
package main

import (
	"fmt"
	"log"

	"github.com/cgxeiji/servo"
)

func main() {
	// Use servo.Close() to close the connection of all servos and pi-blaster.
	defer servo.Close()

	// If you want to move the servos, make sure that pi-blaster is running.
	// For example, start pi-blaster as:
	// $ sudo pi-blaster --gpio 14 --pcm

	// Create a new servo connected to gpio 14.
	myServo := servo.New(14)
	// (optional) Initialize the servo with your preferred values.
	// myServo.Flags = servo.Normalized | servo.Centered
	myServo.MinPulse = 0.05 // Set the minimum pwm pulse width (default: 0.05).
	myServo.MaxPulse = 0.25 // Set the maximum pwm pulse width (default: 0.25).
	myServo.SetPosition(90) // Set the initial position to 90 degrees.
	myServo.SetSpeed(0.2)   // Set the speed to 20% (default: 1.0).
	// NOTE: The maximum speed of the servo is 0.19s/60degrees.
	// (optional) Set a verbose name.
	myServo.Name = "My Servo"

	// Print the information of the servo.
	fmt.Println(myServo)

	// Connect the servo to the daemon.
	err := myServo.Connect()
	if err != nil {
		log.Fatal(err)
	}

	// (optional) Use myServo.Close() to close the connection to a specific
	// servo. You still need to close the connection to pi-blaster with
	// `servo.Close()`.
	defer myServo.Close()

	myServo.SetSpeed(0.5) // Set the speed to half. This is concurrent-safe.
	myServo.MoveTo(180)   // This is a non-blocking call.

	/* do some work */

	myServo.Wait() // Call Wait() to sync with the servo.

	// MoveTo() returns a Waiter interface that can be used to move and wait on
	// the same line.
	myServo.MoveTo(0).Wait() // This is a blocking call.

}
Output:

servo "My Servo" connected to gpio(14) [flags: ( NONE )]

Index

Examples

Constants

View Source
const (
	// Centered sets the range of the servo from -90 to 90 degrees.
	// Together with Normalized, the range of the servo is set to -1 to 1.
	Centered flag = (1 << iota)
	// Normalized sets the range of the servo from 0 to 2.
	// Together with Centered, the range of the servo is set to -1 to 1.
	Normalized
)

Variables

This section is empty.

Functions

func Close

func Close()

Close cleans up the servo package. Make sure to call this in your main goroutine.

func Rate added in v0.1.1

func Rate(r time.Duration)

Rate changes the rate that data is flushed to pi-blaster (default: 40ms). This can be changed on-the-fly.

Types

type Servo

type Servo struct {

	// Name is an optional value to assign a meaningful name to the servo.
	Name string
	// Flags is a bit flag that sets various configuration parameters.
	//
	// servo.Centered sets the range of the servo from -90 to 90 degrees.
	//
	// servo.Normalized sets the range of the servo from 0 to 2.
	// Together with servo.Centered, the range of the servo is set to -1 to 1.
	Flags flag

	// MinPulse is the minimum pwm pulse of the servo. (default 0.05 s)
	// MaxPulse is the maximum pwm pulse of the servo. (default 0.25 s)
	// These calibration variables should be immutables once the servo is
	// connected..
	MinPulse, MaxPulse float64
	// contains filtered or unexported fields
}

Servo is a struct that holds all the information necessary to control a servo motor. Use the function servo.New(gpio) for correct initialization. Servo is designed to be concurrent-safe.

func New

func New(GPIO int) (s *Servo)

New creates a new Servo struct with default values, connected at a GPIO pin of the Raspberry Pi. You should check that the pin is controllable with pi-blaster.

CAUTION: Incorrect pin assignment might cause damage to your Raspberry Pi.

func (*Servo) Close

func (s *Servo) Close()

Close cleans up the state of the servo and deactivates the corresponding GPIO pin.

func (*Servo) Connect

func (s *Servo) Connect() error

Connect connects the servo to the pi-blaster daemon.

func (*Servo) MoveTo

func (s *Servo) MoveTo(target float64) (wait Waiter)

MoveTo sets a target angle for the servo to move. The magnitude of the target depends on the servo's Flags. The target is automatically clamped to the set range. If called concurrently, the target position is overridden by the last goroutine (usually non-deterministic).

func (*Servo) Position

func (s *Servo) Position() float64

Position returns the current angle of the servo, adjusted for its Flags.

func (*Servo) SetPosition

func (s *Servo) SetPosition(position float64)

SetPosition immediately sets the angle the servo.

func (*Servo) SetSpeed

func (s *Servo) SetSpeed(percentage float64)

SetSpeed changes the speed of the servo from (still) 0.0 to 1.0 (max speed). Setting a speed of 0.0 effectively sets the target position to the current position and the servo will not move.

func (*Servo) Stop

func (s *Servo) Stop()

Stop stops moving the servo. This effectively sets the target position to the stopped position of the servo.

func (*Servo) String

func (s *Servo) String() string

String implements the Stringer interface. It returns a string in the following format:

servo "NAME" connected to gpio(GPIO_PIN) [flags: ( FLAGS_SET )]

where NAME is the verbose name (default: fmt.Sprintf("Servo%d", GPIO)), GPIO_PIN is the connection pin of the servo, and FLAGS_SET is the list of flags set (default: NONE).

func (*Servo) Wait

func (s *Servo) Wait()

Wait waits for the servo to stop moving. It is concurrent-safe.

type Waiter

type Waiter interface {
	// Wait waits for the servo to finish moving.
	Wait()
}

Waiter implements the Wait function.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL