Skip to content

Commit

Permalink
gpio(all): introduce functional options
Browse files Browse the repository at this point in the history
  • Loading branch information
gen2thomas committed Dec 2, 2023
1 parent d139c0a commit 64a0bd7
Show file tree
Hide file tree
Showing 43 changed files with 2,308 additions and 1,672 deletions.
6 changes: 3 additions & 3 deletions drivers/aio/analog_sensor_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type AnalogSensorDriver struct {
*driver
sensorCfg *sensorConfiguration
pin string
halt chan bool
halt chan struct{}
gobot.Eventer
lastRawValue int
lastValue float64
Expand Down Expand Up @@ -163,7 +163,7 @@ func (a *AnalogSensorDriver) initialize() error {
// the writing to halt is blocked because there is no immediate read from channel.
// Please note, that this is special behavior caused by the first read is done immediately before the select
// statement.
a.halt = make(chan bool, 1)
a.halt = make(chan struct{}, 1)

oldRawValue := 0
oldValue := 0.0
Expand Down Expand Up @@ -205,7 +205,7 @@ func (a *AnalogSensorDriver) shutdown() error {
// cyclic reading deactivated
return nil
}
a.halt <- true
close(a.halt) // broadcast halt, also to the test
return nil
}

Expand Down
27 changes: 15 additions & 12 deletions drivers/aio/analog_sensor_driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package aio
import (
"fmt"
"strings"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -217,8 +218,8 @@ func TestAnalogSensor_WithSensorCyclicRead(t *testing.T) {
semDone <- true
})

// act: send a halt message
d.halt <- true
// act: send the halt message
require.NoError(t, d.Halt())

// assert: no event
select {
Expand All @@ -234,18 +235,20 @@ func TestAnalogSensorHalt_WithSensorCyclicRead(t *testing.T) {
// arrange
d := NewAnalogSensorDriver(newAioTestAdaptor(), "1", WithSensorCyclicRead(10*time.Millisecond))
require.NoError(t, d.Start())
done := make(chan struct{})
// act & assert
timeout := 2 * d.sensorCfg.readInterval
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
require.NoError(t, d.Halt())
close(done)
defer wg.Done()
select {
case <-d.halt: // wait until halt is broadcasted by close the channel
case <-time.After(timeout): // otherwise run into the timeout
assert.Fail(t, "halt was not received within %s", timeout)
}
}()
// test that the halt is not blocked by any deadlock with mutex and/or channel
select {
case <-done:
case <-time.After(100 * time.Millisecond):
t.Errorf("AnalogSensor was not halted")
}
// act & assert
require.NoError(t, d.Halt())
wg.Wait() // wait until the go function was really finished
}

func TestAnalogSensorCommands_WithSensorScaler(t *testing.T) {
Expand Down
132 changes: 60 additions & 72 deletions drivers/gpio/aip1640_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,168 +22,156 @@ const (
//
// Library ported from: https://github.com/wemos/WEMOS_Matrix_LED_Shield_Arduino_Library
type AIP1640Driver struct {
pinClock *DirectPinDriver
pinData *DirectPinDriver
name string
intensity byte
buffer [8]byte
connection gobot.Connection
gobot.Commander
*driver
pinClock *DirectPinDriver
pinData *DirectPinDriver
intensity byte
buffer [8]byte
}

// NewAIP1640Driver return a new AIP1640Driver given a gobot.Connection and the clock, data and strobe pins
func NewAIP1640Driver(a gobot.Connection, clockPin string, dataPin string) *AIP1640Driver {
t := &AIP1640Driver{
name: gobot.DefaultName("AIP1640Driver"),
pinClock: NewDirectPinDriver(a, clockPin),
pinData: NewDirectPinDriver(a, dataPin),
intensity: 7,
connection: a,
Commander: gobot.NewCommander(),
// NewAIP1640Driver return a new driver for AIP1640 LED driver given a gobot.Connection and the clock,
// data and strobe pins.
//
// Supported options:
//
// "WithName"
func NewAIP1640Driver(a gobot.Connection, clockPin string, dataPin string, opts ...interface{}) *AIP1640Driver {
d := &AIP1640Driver{
driver: newDriver(a, "AIP1640", opts...),
pinClock: NewDirectPinDriver(a, clockPin),
pinData: NewDirectPinDriver(a, dataPin),
intensity: 7,
}
d.afterStart = d.initialize

/* TODO : Add commands */

return t
}

// Start initializes the tm1638, it uses a SPI-like communication protocol
func (a *AIP1640Driver) Start() error {
if err := a.pinData.On(); err != nil {
return err
}
return a.pinClock.On()
}

// Halt implements the Driver interface
func (a *AIP1640Driver) Halt() error { return nil }

// Name returns the AIP1640Drivers name
func (a *AIP1640Driver) Name() string { return a.name }

// SetName sets the AIP1640Drivers name
func (a *AIP1640Driver) SetName(n string) { a.name = n }

// Connection returns the AIP1640Driver Connection
func (a *AIP1640Driver) Connection() gobot.Connection {
return a.connection
return d
}

// SetIntensity changes the intensity (from 1 to 7) of the display
func (a *AIP1640Driver) SetIntensity(level byte) {
func (d *AIP1640Driver) SetIntensity(level byte) {
if level >= 7 {
level = 7
}
a.intensity = level
d.intensity = level
}

// Display sends the buffer to the display (ie. turns on/off the corresponding LEDs)
func (a *AIP1640Driver) Display() error {
func (d *AIP1640Driver) Display() error {
for i := 0; i < 8; i++ {
if err := a.sendData(byte(i), a.buffer[i]); err != nil {
if err := d.sendData(byte(i), d.buffer[i]); err != nil {
return err
}

if err := a.pinData.Off(); err != nil {
if err := d.pinData.Off(); err != nil {
return err
}
if err := a.pinClock.Off(); err != nil {
if err := d.pinClock.Off(); err != nil {
return err
}
time.Sleep(1 * time.Millisecond)
if err := a.pinClock.On(); err != nil {
if err := d.pinClock.On(); err != nil {
return err
}
if err := a.pinData.On(); err != nil {
if err := d.pinData.On(); err != nil {
return err
}
}

return a.sendCommand(AIP1640DispCtrl | a.intensity)
return d.sendCommand(AIP1640DispCtrl | d.intensity)
}

// Clear empties the buffer (turns off all the LEDs)
func (a *AIP1640Driver) Clear() {
func (d *AIP1640Driver) Clear() {
for i := 0; i < 8; i++ {
a.buffer[i] = 0x00
d.buffer[i] = 0x00
}
}

// DrawPixel turns on or off a specific in the buffer
func (a *AIP1640Driver) DrawPixel(x, y byte, enabled bool) {
func (d *AIP1640Driver) DrawPixel(x, y byte, enabled bool) {
if x >= 8 || y >= 8 {
return
}
y = 7 - y
if enabled {
a.buffer[y] |= 1 << x
d.buffer[y] |= 1 << x
} else {
a.buffer[y] &^= 1 << x
d.buffer[y] &^= 1 << x
}
}

// DrawRow sets any given row of LEDs in the buffer
func (a *AIP1640Driver) DrawRow(row, data byte) {
func (d *AIP1640Driver) DrawRow(row, data byte) {
if row >= 8 {
return
}
a.buffer[7-row] = data
d.buffer[7-row] = data
}

// DrawMatrix sets the whole buffer
func (a *AIP1640Driver) DrawMatrix(data [8]byte) {
func (d *AIP1640Driver) DrawMatrix(data [8]byte) {
for i := 0; i < 8; i++ {
a.buffer[7-i] = data[i]
d.buffer[7-i] = data[i]
}
}

// initialize initializes the tm1638, it uses a SPI-like communication protocol
func (d *AIP1640Driver) initialize() error {
if err := d.pinData.On(); err != nil {
return err
}
return d.pinClock.On()
}

// sendCommand is an auxiliary function to send commands to the AIP1640Driver module
func (a *AIP1640Driver) sendCommand(cmd byte) error {
if err := a.pinData.Off(); err != nil {
func (d *AIP1640Driver) sendCommand(cmd byte) error {
if err := d.pinData.Off(); err != nil {
return err
}
if err := a.send(cmd); err != nil {
if err := d.send(cmd); err != nil {
return err
}
return a.pinData.On()
return d.pinData.On()
}

// sendData is an auxiliary function to send data to the AIP1640Driver module
func (a *AIP1640Driver) sendData(address byte, data byte) error {
if err := a.sendCommand(AIP1640DataCmd | AIP1640FixedAddr); err != nil {
func (d *AIP1640Driver) sendData(address byte, data byte) error {
if err := d.sendCommand(AIP1640DataCmd | AIP1640FixedAddr); err != nil {
return err
}
if err := a.pinData.Off(); err != nil {
if err := d.pinData.Off(); err != nil {
return err
}
if err := a.send(AIP1640AddrCmd | address); err != nil {
if err := d.send(AIP1640AddrCmd | address); err != nil {
return err
}
if err := a.send(data); err != nil {
if err := d.send(data); err != nil {
return err
}
return a.pinData.On()
return d.pinData.On()
}

// send writes data on the module
func (a *AIP1640Driver) send(data byte) error {
func (d *AIP1640Driver) send(data byte) error {
for i := 0; i < 8; i++ {
if err := a.pinClock.Off(); err != nil {
if err := d.pinClock.Off(); err != nil {
return err
}

if (data & 1) > 0 {
if err := a.pinData.On(); err != nil {
if err := d.pinData.On(); err != nil {
return err
}
} else {
if err := a.pinData.Off(); err != nil {
if err := d.pinData.Off(); err != nil {
return err
}
}
data >>= 1

if err := a.pinClock.On(); err != nil {
if err := d.pinClock.On(); err != nil {
return err
}
}
Expand Down
Loading

0 comments on commit 64a0bd7

Please sign in to comment.