225 lines
6.0 KiB
Go
225 lines
6.0 KiB
Go
package gpio
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
|
|
"gobot.io/x/gobot/v2"
|
|
)
|
|
|
|
var (
|
|
// ErrServoWriteUnsupported is the error resulting when a driver attempts to use
|
|
// hardware capabilities which a connection does not support
|
|
ErrServoWriteUnsupported = errors.New("ServoWrite is not supported by this platform")
|
|
// ErrPwmWriteUnsupported is the error resulting when a driver attempts to use
|
|
// hardware capabilities which a connection does not support
|
|
ErrPwmWriteUnsupported = errors.New("PwmWrite is not supported by this platform")
|
|
// ErrDigitalWriteUnsupported is the error resulting when a driver attempts to use
|
|
// hardware capabilities which a connection does not support
|
|
ErrDigitalWriteUnsupported = errors.New("DigitalWrite is not supported by this platform")
|
|
// ErrDigitalReadUnsupported is the error resulting when a driver attempts to use
|
|
// hardware capabilities which a connection does not support
|
|
ErrDigitalReadUnsupported = errors.New("DigitalRead is not supported by this platform")
|
|
)
|
|
|
|
const (
|
|
// Error event
|
|
Error = "error"
|
|
// ButtonRelease event
|
|
ButtonRelease = "release"
|
|
// ButtonPush event
|
|
ButtonPush = "push"
|
|
// MotionDetected event
|
|
MotionDetected = "motion-detected"
|
|
// MotionStopped event
|
|
MotionStopped = "motion-stopped"
|
|
)
|
|
|
|
// PwmWriter interface represents an Adaptor which has Pwm capabilities
|
|
type PwmWriter interface {
|
|
PwmWrite(pin string, val byte) error
|
|
}
|
|
|
|
// ServoWriter interface represents an Adaptor which has Servo capabilities
|
|
type ServoWriter interface {
|
|
ServoWrite(pin string, val byte) error
|
|
}
|
|
|
|
// DigitalWriter interface represents an Adaptor which has DigitalWrite capabilities
|
|
type DigitalWriter interface {
|
|
DigitalWrite(pin string, val byte) error
|
|
}
|
|
|
|
// DigitalReader interface represents an Adaptor which has DigitalRead capabilities
|
|
type DigitalReader interface {
|
|
DigitalRead(pin string) (val int, err error)
|
|
}
|
|
|
|
// optionApplier needs to be implemented by each configurable option type
|
|
type optionApplier interface {
|
|
apply(cfg *configuration)
|
|
}
|
|
|
|
// configuration contains all changeable attributes of the driver.
|
|
type configuration struct {
|
|
name string
|
|
pin string
|
|
}
|
|
|
|
// nameOption is the type for applying another name to the configuration
|
|
type nameOption string
|
|
|
|
// pinOption is the type for applying a pin to the configuration
|
|
type pinOption string
|
|
|
|
// Driver implements the interface gobot.Driver.
|
|
type driver struct {
|
|
driverCfg *configuration
|
|
connection gobot.Adaptor
|
|
afterStart func() error
|
|
beforeHalt func() error
|
|
gobot.Commander
|
|
mutex *sync.Mutex // mutex often needed to ensure that write-read sequences are not interrupted
|
|
}
|
|
|
|
// newDriver creates a new generic and basic gpio gobot driver.
|
|
//
|
|
// Supported options:
|
|
//
|
|
// "WithName"
|
|
// "withPin"
|
|
func newDriver(a gobot.Adaptor, name string, opts ...interface{}) *driver {
|
|
d := &driver{
|
|
driverCfg: &configuration{name: gobot.DefaultName(name)},
|
|
connection: a,
|
|
afterStart: func() error { return nil },
|
|
beforeHalt: func() error { return nil },
|
|
Commander: gobot.NewCommander(),
|
|
mutex: &sync.Mutex{},
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
switch o := opt.(type) {
|
|
case optionApplier:
|
|
o.apply(d.driverCfg)
|
|
default:
|
|
panic(fmt.Sprintf("'%s' can not be applied on '%s'", opt, d.driverCfg.name))
|
|
}
|
|
}
|
|
|
|
return d
|
|
}
|
|
|
|
// WithName is used to replace the default name of the driver.
|
|
func WithName(name string) optionApplier {
|
|
return nameOption(name)
|
|
}
|
|
|
|
// withPin is used to add a pin to the driver. Only one pin can be linked.
|
|
// This option is not available outside gpio package.
|
|
func withPin(pin string) optionApplier {
|
|
return pinOption(pin)
|
|
}
|
|
|
|
// Name returns the name of the gpio device.
|
|
func (d *driver) Name() string {
|
|
return d.driverCfg.name
|
|
}
|
|
|
|
// SetName sets the name of the gpio device.
|
|
// Deprecated: Please use option [gpio.WithName] instead.
|
|
func (d *driver) SetName(name string) {
|
|
WithName(name).apply(d.driverCfg)
|
|
}
|
|
|
|
// Pin returns the pin associated with the driver.
|
|
func (d *driver) Pin() string {
|
|
return d.driverCfg.pin
|
|
}
|
|
|
|
// Connection returns the connection of the gpio device.
|
|
func (d *driver) Connection() gobot.Connection {
|
|
if conn, ok := d.connection.(gobot.Connection); ok {
|
|
return conn
|
|
}
|
|
|
|
log.Printf("%s has no gobot connection\n", d.driverCfg.name)
|
|
return nil
|
|
}
|
|
|
|
// Start initializes the gpio device.
|
|
func (d *driver) Start() error {
|
|
d.mutex.Lock()
|
|
defer d.mutex.Unlock()
|
|
|
|
// currently there is nothing to do here for the driver
|
|
|
|
return d.afterStart()
|
|
}
|
|
|
|
// Halt halts the gpio device.
|
|
func (d *driver) Halt() error {
|
|
d.mutex.Lock()
|
|
defer d.mutex.Unlock()
|
|
|
|
// currently there is nothing to do after halt for the driver
|
|
|
|
return d.beforeHalt()
|
|
}
|
|
|
|
// digitalRead is a helper function with check that the connection implements DigitalReader
|
|
func (d *driver) digitalRead(pin string) (int, error) {
|
|
if reader, ok := d.connection.(DigitalReader); ok {
|
|
return reader.DigitalRead(pin)
|
|
}
|
|
|
|
return 0, ErrDigitalReadUnsupported
|
|
}
|
|
|
|
// digitalWrite is a helper function with check that the connection implements DigitalWriter
|
|
func (d *driver) digitalWrite(pin string, val byte) error {
|
|
if writer, ok := d.connection.(DigitalWriter); ok {
|
|
return writer.DigitalWrite(pin, val)
|
|
}
|
|
|
|
return ErrDigitalWriteUnsupported
|
|
}
|
|
|
|
// pwmWrite is a helper function with check that the connection implements PwmWriter
|
|
func (d *driver) pwmWrite(pin string, level byte) error {
|
|
if writer, ok := d.connection.(PwmWriter); ok {
|
|
return writer.PwmWrite(pin, level)
|
|
}
|
|
|
|
return ErrPwmWriteUnsupported
|
|
}
|
|
|
|
// servoWrite is a helper function with check that the connection implements ServoWriter
|
|
func (d *driver) servoWrite(pin string, level byte) error {
|
|
if writer, ok := d.connection.(ServoWriter); ok {
|
|
return writer.ServoWrite(pin, level)
|
|
}
|
|
|
|
return ErrServoWriteUnsupported
|
|
}
|
|
|
|
func (o nameOption) String() string {
|
|
return "name option for digital drivers"
|
|
}
|
|
|
|
func (o pinOption) String() string {
|
|
return "pin option for digital drivers"
|
|
}
|
|
|
|
// apply change the name in the configuration.
|
|
func (o nameOption) apply(c *configuration) {
|
|
c.name = string(o)
|
|
}
|
|
|
|
// apply change the pins list of the configuration.
|
|
func (o pinOption) apply(c *configuration) {
|
|
c.pin = string(o)
|
|
}
|