Skip to content

Commit

Permalink
Bugfix/Improvement: Use PWMPinsAdaptor for platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
gen2thomas authored Dec 5, 2022
1 parent 89afbcf commit 9cbc7ac
Show file tree
Hide file tree
Showing 35 changed files with 1,666 additions and 1,209 deletions.
39 changes: 19 additions & 20 deletions adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,26 @@ type DigitalPinnerProvider interface {

// PWMPinner is the interface for system PWM interactions
type PWMPinner interface {
// Export exports the pin for use by the operating system
// Export exports the PWM pin for use by the operating system
Export() error
// Unexport unexports the pin and releases the pin from the operating system
// Unexport releases the PWM pin from the operating system
Unexport() error
// Enable enables/disables the PWM pin
// TODO: rename to "SetEnable(bool)" according to golang style and allow "Enable()" to be the getter function
Enable(bool) (err error)
// Polarity returns the polarity either normal or inverted
Polarity() (polarity string, err error)
// SetPolarity writes value to pwm polarity path
SetPolarity(value string) (err error)
// InvertPolarity sets the polarity to inverted if called with true
InvertPolarity(invert bool) (err error)
// Period returns the current PWM period for pin
Period() (period uint32, err error)
// SetPeriod sets the current PWM period for pin
SetPeriod(period uint32) (err error)
// DutyCycle returns the duty cycle for the pin
DutyCycle() (duty uint32, err error)
// SetDutyCycle writes the duty cycle to the pin
SetDutyCycle(duty uint32) (err error)
// Enabled returns the enabled state of the PWM pin
Enabled() (bool, error)
// SetEnabled enables/disables the PWM pin
SetEnabled(bool) error
// Polarity returns true if the polarity of the PWM pin is normal, otherwise false
Polarity() (bool, error)
// SetPolarity sets the polarity of the PWM pin to normal if called with true and to inverted if called with false
SetPolarity(normal bool) error
// Period returns the current PWM period in nanoseconds for pin
Period() (uint32, error)
// SetPeriod sets the current PWM period in nanoseconds for pin
SetPeriod(uint32) error
// DutyCycle returns the duty cycle in nanoseconds for the PWM pin
DutyCycle() (uint32, error)
// SetDutyCycle writes the duty cycle in nanoseconds to the PWM pin
SetDutyCycle(uint32) error
}

// PWMPinnerProvider is the interface that an Adaptor should implement to allow
Expand All @@ -80,7 +79,7 @@ type Adaptor interface {
// Name returns the label for the Adaptor
Name() string
// SetName sets the label for the Adaptor
SetName(n string)
SetName(string)
// Connect initiates the Adaptor
Connect() error
// Finalize terminates the Adaptor
Expand Down
51 changes: 26 additions & 25 deletions platforms/adaptors/digitalpinsadaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,43 @@ import (
"gobot.io/x/gobot/system"
)

type translator func(pin string) (chip string, line int, err error)
type creator func(chip string, line int, o ...func(gobot.DigitalPinOptioner) bool) gobot.DigitalPinner
type digitalPinTranslator func(pin string) (chip string, line int, err error)
type digitalPinInitializer func(gobot.DigitalPinner) error

type digitalPinsOption interface {
setCreator(creator) // needed e.g. by Beaglebone adaptor
setDigitalPinInitializer(digitalPinInitializer)
}

// DigitalPinsAdaptor is a adaptor for digital pins, normally used for composition in platforms.
type DigitalPinsAdaptor struct {
sys *system.Accesser
translate translator
create creator
pins map[string]gobot.DigitalPinner
mutex sync.Mutex
sys *system.Accesser
translate digitalPinTranslator
initialize digitalPinInitializer
pins map[string]gobot.DigitalPinner
mutex sync.Mutex
}

// NewDigitalPinsAdaptor provides the access to digital pins of the board. It supports sysfs and gpiod system drivers.
// This is decided by the given accesser. The translator is used to adapt the header naming, which is given by user, to
// the internal file name or chip/line nomenclature. This varies by each platform. If for some reasons the default
// creator is not suitable, it can be given by the option "WithPinCreator()". This is especially needed, if some values
// needs to be adjusted after the pin was created. E.g. for Beaglebone platform.
func NewDigitalPinsAdaptor(sys *system.Accesser, t translator, options ...func(digitalPinsOption)) *DigitalPinsAdaptor {
s := &DigitalPinsAdaptor{
translate: t,
create: sys.NewDigitalPin,
// This is decided by the given accesser. The translator is used to adapt the pin header naming, which is given by user,
// to the internal file name or chip/line nomenclature. This varies by each platform. If for some reasons the default
// initializer is not suitable, it can be given by the option "WithDigitalPinInitializer()". This is especially needed,
// if some values needs to be adjusted after the pin was created but before the pin is exported.
func NewDigitalPinsAdaptor(sys *system.Accesser, t digitalPinTranslator, options ...func(digitalPinsOption)) *DigitalPinsAdaptor {
a := &DigitalPinsAdaptor{
sys: sys,
translate: t,
initialize: func(pin gobot.DigitalPinner) error { return pin.Export() },
}
for _, option := range options {
option(s)
option(a)
}
return s
return a
}

// WithPinCreator can be used to substitute the default creator.
func WithPinCreator(pc creator) func(digitalPinsOption) {
// WithDigitalPinInitializer can be used to substitute the default initializer.
func WithDigitalPinInitializer(pc digitalPinInitializer) func(digitalPinsOption) {
return func(a digitalPinsOption) {
a.setCreator(pc)
a.setDigitalPinInitializer(pc)
}
}

Expand Down Expand Up @@ -106,8 +107,8 @@ func (a *DigitalPinsAdaptor) DigitalWrite(id string, val byte) error {
return pin.Write(int(val))
}

func (a *DigitalPinsAdaptor) setCreator(pc creator) {
a.create = pc
func (a *DigitalPinsAdaptor) setDigitalPinInitializer(pinInit digitalPinInitializer) {
a.initialize = pinInit
}

func (a *DigitalPinsAdaptor) digitalPin(id string, o ...func(gobot.DigitalPinOptioner) bool) (gobot.DigitalPinner, error) {
Expand All @@ -122,8 +123,8 @@ func (a *DigitalPinsAdaptor) digitalPin(id string, o ...func(gobot.DigitalPinOpt
if err != nil {
return nil, err
}
pin = a.create(chip, line, o...)
if err = pin.Export(); err != nil {
pin = a.sys.NewDigitalPin(chip, line, o...)
if err = a.initialize(pin); err != nil {
return nil, err
}
a.pins[id] = pin
Expand Down
50 changes: 26 additions & 24 deletions platforms/adaptors/digitalpinsadaptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@ var _ gobot.DigitalPinnerProvider = (*DigitalPinsAdaptor)(nil)
var _ gpio.DigitalReader = (*DigitalPinsAdaptor)(nil)
var _ gpio.DigitalWriter = (*DigitalPinsAdaptor)(nil)

func initTestAdaptorWithMockedFilesystem(mockPaths []string) (*DigitalPinsAdaptor, *system.MockFilesystem) {
func initTestDigitalPinsAdaptorWithMockedFilesystem(mockPaths []string) (*DigitalPinsAdaptor, *system.MockFilesystem) {
sys := system.NewAccesser()
fs := sys.UseMockFilesystem(mockPaths)
a := NewDigitalPinsAdaptor(sys, testTranslator)
a := NewDigitalPinsAdaptor(sys, testDigitalPinTranslator)
if err := a.Connect(); err != nil {
panic(err)
}
return a, fs
}

func testTranslator(pin string) (string, int, error) {
func testDigitalPinTranslator(pin string) (string, int, error) {
line, err := strconv.Atoi(pin)
if err != nil {
return "", 0, fmt.Errorf("not a valid pin")
Expand All @@ -37,27 +40,36 @@ func testTranslator(pin string) (string, int, error) {
return "", line, err
}

func TestConnect(t *testing.T) {
func TestDigitalPinsConnect(t *testing.T) {
translate := func(pin string) (chip string, line int, err error) { return }
sys := system.NewAccesser()

a := NewDigitalPinsAdaptor(sys, translate)
gobottest.Assert(t, a.pins, (map[string]gobot.DigitalPinner)(nil))

a.Connect()
_, err := a.DigitalRead("13")
gobottest.Assert(t, err.Error(), "not connected")

err = a.DigitalWrite("7", 1)
gobottest.Assert(t, err.Error(), "not connected")

err = a.Connect()
gobottest.Assert(t, err, nil)
gobottest.Refute(t, a.pins, (map[string]gobot.DigitalPinner)(nil))
gobottest.Assert(t, len(a.pins), 0)
}

func TestFinalize(t *testing.T) {
func TestDigitalPinsFinalize(t *testing.T) {
// arrange
mockedPaths := []string{
"/sys/class/gpio/export",
"/sys/class/gpio/unexport",
"/sys/class/gpio/gpio14/direction",
"/sys/class/gpio/gpio14/value",
}
a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths)
sys := system.NewAccesser()
fs := sys.UseMockFilesystem(mockedPaths)
a := NewDigitalPinsAdaptor(sys, testDigitalPinTranslator)
// assert that finalize before connect is working
gobottest.Assert(t, a.Finalize(), nil)
// arrange
Expand All @@ -79,16 +91,15 @@ func TestFinalize(t *testing.T) {
gobottest.Assert(t, strings.Contains(err.Error(), "/sys/class/gpio/unexport: No such file"), true)
}

func TestReConnect(t *testing.T) {
func TestDigitalPinsReConnect(t *testing.T) {
// arrange
mockedPaths := []string{
"/sys/class/gpio/export",
"/sys/class/gpio/unexport",
"/sys/class/gpio/gpio15/direction",
"/sys/class/gpio/gpio15/value",
}
a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths)
a.Connect()
a, _ := initTestDigitalPinsAdaptorWithMockedFilesystem(mockedPaths)
gobottest.Assert(t, a.DigitalWrite("4", 1), nil)
gobottest.Assert(t, len(a.pins), 1)
gobottest.Assert(t, a.Finalize(), nil)
Expand All @@ -106,8 +117,7 @@ func TestDigitalIO(t *testing.T) {
"/sys/class/gpio/gpio25/value",
"/sys/class/gpio/gpio25/direction",
}
a, _ := initTestAdaptorWithMockedFilesystem(mockedPaths)
a.Connect()
a, _ := initTestDigitalPinsAdaptorWithMockedFilesystem(mockedPaths)

err := a.DigitalWrite("14", 1)
gobottest.Assert(t, err, nil)
Expand All @@ -124,14 +134,10 @@ func TestDigitalRead(t *testing.T) {
"/sys/class/gpio/gpio24/value",
"/sys/class/gpio/gpio24/direction",
}
a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths)
a, fs := initTestDigitalPinsAdaptorWithMockedFilesystem(mockedPaths)
fs.Files["/sys/class/gpio/gpio24/value"].Contents = "1"
i, err := a.DigitalRead("13")
gobottest.Assert(t, err.Error(), "not connected")

a.Connect()

i, err = a.DigitalRead("13")
i, err := a.DigitalRead("13")
gobottest.Assert(t, err, nil)
gobottest.Assert(t, i, 1)

Expand All @@ -151,13 +157,9 @@ func TestDigitalWrite(t *testing.T) {
"/sys/class/gpio/gpio18/value",
"/sys/class/gpio/gpio18/direction",
}
a, fs := initTestAdaptorWithMockedFilesystem(mockedPaths)
err := a.DigitalWrite("7", 1)
gobottest.Assert(t, err.Error(), "not connected")

a.Connect()
a, fs := initTestDigitalPinsAdaptorWithMockedFilesystem(mockedPaths)

err = a.DigitalWrite("7", 1)
err := a.DigitalWrite("7", 1)
gobottest.Assert(t, err, nil)
gobottest.Assert(t, fs.Files["/sys/class/gpio/gpio18/value"].Contents, "1")

Expand Down
Loading

0 comments on commit 9cbc7ac

Please sign in to comment.