Skip to content

Commit

Permalink
tinkerboard2: introduce adapter for Asus Tinker Board 2 (#1108)
Browse files Browse the repository at this point in the history
  • Loading branch information
gen2thomas authored Dec 17, 2024
1 parent 29893b1 commit c1a11a6
Show file tree
Hide file tree
Showing 45 changed files with 1,236 additions and 1,507 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ platforms are currently supported:
- [Sphero Ollie](http://www.sphero.com/ollie) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/sphero/ollie)
- [Sphero SPRK+](http://www.sphero.com/sprk-plus) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/sphero/sprkplus)
- [Tinker Board](https://www.asus.com/us/Single-Board-Computer/Tinker-Board/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/tinkerboard)
- [Tinker Board 2](https://tinker-board.asus.com/series/tinker-board-2.html/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/tinkerboard/tinkerboard2)
- [UP2](http://www.up-board.org/upsquared/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/upboard/up2)

Support for many devices that use Analog Input/Output (AIO) have a shared set of drivers provided using
Expand Down
89 changes: 89 additions & 0 deletions examples/tinkerboard2_direct_pin_bin_counter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//go:build example
// +build example

//
// Do not build by default.

package main

import (
"fmt"
"time"

"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/gpio"
"gobot.io/x/gobot/v2/platforms/adaptors"
"gobot.io/x/gobot/v2/platforms/tinkerboard/tinkerboard2"
)

// Wiring
// PWR Tinkerboard-2: 1 (+3.3V, VCC), 2(+5V), 6, 9, 14, 20 (GND)
// GPIO Tinkerboard-2: header pins 3, 5, 7, 11 used as inverted output
// LED's: the output pins are wired to the cathode of a LED, the anode is wired with a resistor (70-130Ohm for 20mA)
// to +3.3V (use >150Ohm if connected to +5V)
// Expected behavior: the 4 LED's on normal output counts up binary
func main() {
const (
outPinBit0Num = "3"
outPinBit1Num = "5"
outPinBit2Num = "7"
outPinBit3Num = "11"
)

board := tinkerboard2.NewAdaptor(adaptors.WithGpiosActiveLow(outPinBit0Num, outPinBit1Num, outPinBit2Num,
outPinBit3Num))
outPinB0 := gpio.NewDirectPinDriver(board, outPinBit0Num)
outPinB1 := gpio.NewDirectPinDriver(board, outPinBit1Num)
outPinB2 := gpio.NewDirectPinDriver(board, outPinBit2Num)
outPinB3 := gpio.NewDirectPinDriver(board, outPinBit3Num)

work := func() {
value := byte(0)

gobot.Every(500*time.Millisecond, func() {
b0 := value & 0x01
b1 := (value & 0x02) / 0x02
b2 := (value & 0x04) / 0x04
b3 := (value & 0x08) / 0x08

if err := outPinB0.DigitalWrite(b0); err != nil {
fmt.Println(err)
} else {
fmt.Printf("pin %s is now %d\n", outPinBit0Num, b0)
}

if err := outPinB1.DigitalWrite(b1); err != nil {
fmt.Println(err)
} else {
fmt.Printf("pin %s is now %d\n", outPinBit1Num, b1)
}

if err := outPinB2.DigitalWrite(b2); err != nil {
fmt.Println(err)
} else {
fmt.Printf("pin %s is now %d\n", outPinBit2Num, b2)
}

if err := outPinB3.DigitalWrite(b3); err != nil {
fmt.Println(err)
} else {
fmt.Printf("pin %s is now %d\n", outPinBit3Num, b3)
}

value++
if value > 15 {
value = 0
}
})
}

robot := gobot.NewRobot("pinBot",
[]gobot.Connection{board},
[]gobot.Device{outPinB0, outPinB1, outPinB2, outPinB3},
work,
)

if err := robot.Start(); err != nil {
panic(err)
}
}
80 changes: 80 additions & 0 deletions examples/tinkerboard2_yl40.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//go:build example
// +build example

//
// Do not build by default.

package main

import (
"fmt"
"log"
"time"

"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/i2c"
"gobot.io/x/gobot/v2/platforms/tinkerboard/tinkerboard2"
)

func main() {
// Wiring
// PWR Tinkerboard 2: 1 (+3.3V, VCC), 6, 9, 14, 20 (GND)
// I2C1 Tinkerboard 2: 27 (SDA), 28 (SCL)
// YL-40 module: wire AOUT --> AIN2 for this example, set all jumpers for temp, LDR and variable resistor
//
// Note: temperature measurement is often buggy, because sensor is not properly grounded
// fix it by soldering a small bridge to the adjacent ground pin of brightness sensor
board := tinkerboard2.NewAdaptor()
yl := i2c.NewYL40Driver(board, i2c.WithBus(7))

work := func() {
// the LED light is visible above ~1.7V
writeVal, _ := yl.AOUT()

gobot.Every(1000*time.Millisecond, func() {
if err := yl.Write(writeVal); err != nil {
fmt.Println(err)
} else {
log.Printf(" %.1f V written", writeVal)
writeVal = writeVal + 0.1
if writeVal > 3.3 {
writeVal = 0
}
}

if brightness, err := yl.ReadBrightness(); err != nil {
fmt.Println(err)
} else {
log.Printf("Brightness: %.0f [0..1000]", brightness)
}

if temperature, err := yl.ReadTemperature(); err != nil {
fmt.Println(err)
} else {
log.Printf("Temperature: %.1f °C", temperature)
}

if ain2, err := yl.ReadAIN2(); err != nil {
fmt.Println(err)
} else {
log.Printf("Read back AOUT: %.1f [0..3.3]", ain2)
}

if potiState, err := yl.ReadPotentiometer(); err != nil {
fmt.Println(err)
} else {
log.Printf("Resistor: %.0f %% [-100..+100]", potiState)
}
})
}

robot := gobot.NewRobot("yl40Bot",
[]gobot.Connection{board},
[]gobot.Device{yl},
work,
)

if err := robot.Start(); err != nil {
panic(err)
}
}
6 changes: 3 additions & 3 deletions platforms/adaptors/analogpinsadaptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func testAnalogPinTranslator(id string) (string, bool, bool, uint16, error) {
return analogReadWriteStringPath, true, true, 13, nil
}

return "", false, false, 0, fmt.Errorf("'%s' is not a valid id of a analog pin", id)
return "", false, false, 0, fmt.Errorf("'%s' is not a valid id of an analog pin", id)
}

func TestAnalogPinsConnect(t *testing.T) {
Expand Down Expand Up @@ -158,7 +158,7 @@ func TestAnalogWrite(t *testing.T) {
wantValW: "0",
wantValRW: "30000",
wantValRWS: "inverted",
wantErr: "'notexist' is not a valid id of a analog pin",
wantErr: "'notexist' is not a valid id of an analog pin",
},
"error_write_not_allowed": {
pin: "read",
Expand Down Expand Up @@ -218,7 +218,7 @@ func TestAnalogRead(t *testing.T) {
},
"error_notexist": {
pin: "notexist",
wantErr: "'notexist' is not a valid id of a analog pin",
wantErr: "'notexist' is not a valid id of an analog pin",
},
"error_invalid_syntax": {
pin: "read/write_string",
Expand Down
45 changes: 45 additions & 0 deletions platforms/adaptors/analogpintranslator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package adaptors

import (
"fmt"

"gobot.io/x/gobot/v2/system"
)

type AnalogPinDefinition struct {
Path string
R bool // readable
W bool // writable
BufLen uint16
}

type AnalogPinDefinitions map[string]AnalogPinDefinition

type AnalogPinTranslator struct {
sys *system.Accesser
pinDefinitions AnalogPinDefinitions
}

// NewAnalogPinTranslator creates a new instance of a translator for analog pins, suitable for the most cases.
func NewAnalogPinTranslator(sys *system.Accesser, pinDefinitions AnalogPinDefinitions) *AnalogPinTranslator {
return &AnalogPinTranslator{sys: sys, pinDefinitions: pinDefinitions}
}

// Translate returns the sysfs path for the given id.
func (pt *AnalogPinTranslator) Translate(id string) (string, bool, bool, uint16, error) {
pinInfo, ok := pt.pinDefinitions[id]
if !ok {
return "", false, false, 0, fmt.Errorf("'%s' is not a valid id for an analog pin", id)
}

path := pinInfo.Path
info, err := pt.sys.Stat(path)
if err != nil {
return "", false, false, 0, fmt.Errorf("Error (%v) on access '%s'", err, path)
}
if info.IsDir() {
return "", false, false, 0, fmt.Errorf("The item '%s' is a directory, which is not expected", path)
}

return path, pinInfo.R, pinInfo.W, pinInfo.BufLen, nil
}
77 changes: 77 additions & 0 deletions platforms/adaptors/analogpintranslator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package adaptors

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"gobot.io/x/gobot/v2/system"
)

func TestNewAnalogPinTranslator(t *testing.T) {
// arrange
sys := &system.Accesser{}
pinDef := AnalogPinDefinitions{}
// act
pt := NewAnalogPinTranslator(sys, pinDef)
// assert
assert.IsType(t, &AnalogPinTranslator{}, pt)
assert.Equal(t, sys, pt.sys)
assert.Equal(t, pinDef, pt.pinDefinitions)
}

func TestAnalogPinTranslatorTranslate(t *testing.T) {
pinDefinitions := AnalogPinDefinitions{
"thermal_zone0": {Path: "/sys/class/thermal/thermal_zone0/temp", R: true, W: false, BufLen: 7},
"thermal_zone1": {Path: "/sys/class/thermal/thermal_zone1/temp", R: true, W: false, BufLen: 7},
}
mockedPaths := []string{
"/sys/class/thermal/thermal_zone0/temp",
"/sys/class/thermal/thermal_zone1/temp",
}
tests := map[string]struct {
id string
wantPath string
wantReadable bool
wantBufLen uint16
wantErr string
}{
"translate_thermal_zone0": {
id: "thermal_zone0",
wantPath: "/sys/class/thermal/thermal_zone0/temp",
wantReadable: true,
wantBufLen: 7,
},
"translate_thermal_zone1": {
id: "thermal_zone1",
wantPath: "/sys/class/thermal/thermal_zone1/temp",
wantReadable: true,
wantBufLen: 7,
},
"unknown_id": {
id: "99",
wantErr: "'99' is not a valid id for an analog pin",
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
// arrange
sys := system.NewAccesser()
_ = sys.UseMockFilesystem(mockedPaths)
pt := NewAnalogPinTranslator(sys, pinDefinitions)
// act
path, r, w, buf, err := pt.Translate(tc.id)
// assert
if tc.wantErr != "" {
require.EqualError(t, err, tc.wantErr)
} else {
require.NoError(t, err)
}
assert.Equal(t, tc.wantPath, path)
assert.Equal(t, tc.wantReadable, r)
assert.False(t, w)
assert.Equal(t, tc.wantBufLen, buf)
})
}
}
22 changes: 22 additions & 0 deletions platforms/adaptors/busnumbervalidator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package adaptors

import "fmt"

type BusNumberValidator struct {
validNumbers []int
}

// NewBusNumberValidator creates a new instance for a bus number validator, used for I2C and SPI.
func NewBusNumberValidator(validNumbers []int) *BusNumberValidator {
return &BusNumberValidator{validNumbers: validNumbers}
}

func (bnv *BusNumberValidator) Validate(busNr int) error {
for _, validNumber := range bnv.validNumbers {
if validNumber == busNr {
return nil
}
}

return fmt.Errorf("Bus number %d out of range %v", busNr, bnv.validNumbers)
}
Loading

0 comments on commit c1a11a6

Please sign in to comment.