Skip to content

Commit c1a11a6

Browse files
authored
tinkerboard2: introduce adapter for Asus Tinker Board 2 (#1108)
1 parent 29893b1 commit c1a11a6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1236
-1507
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ platforms are currently supported:
281281
- [Sphero Ollie](http://www.sphero.com/ollie) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/sphero/ollie)
282282
- [Sphero SPRK+](http://www.sphero.com/sprk-plus) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/sphero/sprkplus)
283283
- [Tinker Board](https://www.asus.com/us/Single-Board-Computer/Tinker-Board/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/tinkerboard)
284+
- [Tinker Board 2](https://tinker-board.asus.com/series/tinker-board-2.html/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/tinkerboard/tinkerboard2)
284285
- [UP2](http://www.up-board.org/upsquared/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/upboard/up2)
285286

286287
Support for many devices that use Analog Input/Output (AIO) have a shared set of drivers provided using
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//go:build example
2+
// +build example
3+
4+
//
5+
// Do not build by default.
6+
7+
package main
8+
9+
import (
10+
"fmt"
11+
"time"
12+
13+
"gobot.io/x/gobot/v2"
14+
"gobot.io/x/gobot/v2/drivers/gpio"
15+
"gobot.io/x/gobot/v2/platforms/adaptors"
16+
"gobot.io/x/gobot/v2/platforms/tinkerboard/tinkerboard2"
17+
)
18+
19+
// Wiring
20+
// PWR Tinkerboard-2: 1 (+3.3V, VCC), 2(+5V), 6, 9, 14, 20 (GND)
21+
// GPIO Tinkerboard-2: header pins 3, 5, 7, 11 used as inverted output
22+
// LED's: the output pins are wired to the cathode of a LED, the anode is wired with a resistor (70-130Ohm for 20mA)
23+
// to +3.3V (use >150Ohm if connected to +5V)
24+
// Expected behavior: the 4 LED's on normal output counts up binary
25+
func main() {
26+
const (
27+
outPinBit0Num = "3"
28+
outPinBit1Num = "5"
29+
outPinBit2Num = "7"
30+
outPinBit3Num = "11"
31+
)
32+
33+
board := tinkerboard2.NewAdaptor(adaptors.WithGpiosActiveLow(outPinBit0Num, outPinBit1Num, outPinBit2Num,
34+
outPinBit3Num))
35+
outPinB0 := gpio.NewDirectPinDriver(board, outPinBit0Num)
36+
outPinB1 := gpio.NewDirectPinDriver(board, outPinBit1Num)
37+
outPinB2 := gpio.NewDirectPinDriver(board, outPinBit2Num)
38+
outPinB3 := gpio.NewDirectPinDriver(board, outPinBit3Num)
39+
40+
work := func() {
41+
value := byte(0)
42+
43+
gobot.Every(500*time.Millisecond, func() {
44+
b0 := value & 0x01
45+
b1 := (value & 0x02) / 0x02
46+
b2 := (value & 0x04) / 0x04
47+
b3 := (value & 0x08) / 0x08
48+
49+
if err := outPinB0.DigitalWrite(b0); err != nil {
50+
fmt.Println(err)
51+
} else {
52+
fmt.Printf("pin %s is now %d\n", outPinBit0Num, b0)
53+
}
54+
55+
if err := outPinB1.DigitalWrite(b1); err != nil {
56+
fmt.Println(err)
57+
} else {
58+
fmt.Printf("pin %s is now %d\n", outPinBit1Num, b1)
59+
}
60+
61+
if err := outPinB2.DigitalWrite(b2); err != nil {
62+
fmt.Println(err)
63+
} else {
64+
fmt.Printf("pin %s is now %d\n", outPinBit2Num, b2)
65+
}
66+
67+
if err := outPinB3.DigitalWrite(b3); err != nil {
68+
fmt.Println(err)
69+
} else {
70+
fmt.Printf("pin %s is now %d\n", outPinBit3Num, b3)
71+
}
72+
73+
value++
74+
if value > 15 {
75+
value = 0
76+
}
77+
})
78+
}
79+
80+
robot := gobot.NewRobot("pinBot",
81+
[]gobot.Connection{board},
82+
[]gobot.Device{outPinB0, outPinB1, outPinB2, outPinB3},
83+
work,
84+
)
85+
86+
if err := robot.Start(); err != nil {
87+
panic(err)
88+
}
89+
}

examples/tinkerboard2_yl40.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//go:build example
2+
// +build example
3+
4+
//
5+
// Do not build by default.
6+
7+
package main
8+
9+
import (
10+
"fmt"
11+
"log"
12+
"time"
13+
14+
"gobot.io/x/gobot/v2"
15+
"gobot.io/x/gobot/v2/drivers/i2c"
16+
"gobot.io/x/gobot/v2/platforms/tinkerboard/tinkerboard2"
17+
)
18+
19+
func main() {
20+
// Wiring
21+
// PWR Tinkerboard 2: 1 (+3.3V, VCC), 6, 9, 14, 20 (GND)
22+
// I2C1 Tinkerboard 2: 27 (SDA), 28 (SCL)
23+
// YL-40 module: wire AOUT --> AIN2 for this example, set all jumpers for temp, LDR and variable resistor
24+
//
25+
// Note: temperature measurement is often buggy, because sensor is not properly grounded
26+
// fix it by soldering a small bridge to the adjacent ground pin of brightness sensor
27+
board := tinkerboard2.NewAdaptor()
28+
yl := i2c.NewYL40Driver(board, i2c.WithBus(7))
29+
30+
work := func() {
31+
// the LED light is visible above ~1.7V
32+
writeVal, _ := yl.AOUT()
33+
34+
gobot.Every(1000*time.Millisecond, func() {
35+
if err := yl.Write(writeVal); err != nil {
36+
fmt.Println(err)
37+
} else {
38+
log.Printf(" %.1f V written", writeVal)
39+
writeVal = writeVal + 0.1
40+
if writeVal > 3.3 {
41+
writeVal = 0
42+
}
43+
}
44+
45+
if brightness, err := yl.ReadBrightness(); err != nil {
46+
fmt.Println(err)
47+
} else {
48+
log.Printf("Brightness: %.0f [0..1000]", brightness)
49+
}
50+
51+
if temperature, err := yl.ReadTemperature(); err != nil {
52+
fmt.Println(err)
53+
} else {
54+
log.Printf("Temperature: %.1f °C", temperature)
55+
}
56+
57+
if ain2, err := yl.ReadAIN2(); err != nil {
58+
fmt.Println(err)
59+
} else {
60+
log.Printf("Read back AOUT: %.1f [0..3.3]", ain2)
61+
}
62+
63+
if potiState, err := yl.ReadPotentiometer(); err != nil {
64+
fmt.Println(err)
65+
} else {
66+
log.Printf("Resistor: %.0f %% [-100..+100]", potiState)
67+
}
68+
})
69+
}
70+
71+
robot := gobot.NewRobot("yl40Bot",
72+
[]gobot.Connection{board},
73+
[]gobot.Device{yl},
74+
work,
75+
)
76+
77+
if err := robot.Start(); err != nil {
78+
panic(err)
79+
}
80+
}

platforms/adaptors/analogpinsadaptor_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func testAnalogPinTranslator(id string) (string, bool, bool, uint16, error) {
5252
return analogReadWriteStringPath, true, true, 13, nil
5353
}
5454

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

5858
func TestAnalogPinsConnect(t *testing.T) {
@@ -158,7 +158,7 @@ func TestAnalogWrite(t *testing.T) {
158158
wantValW: "0",
159159
wantValRW: "30000",
160160
wantValRWS: "inverted",
161-
wantErr: "'notexist' is not a valid id of a analog pin",
161+
wantErr: "'notexist' is not a valid id of an analog pin",
162162
},
163163
"error_write_not_allowed": {
164164
pin: "read",
@@ -218,7 +218,7 @@ func TestAnalogRead(t *testing.T) {
218218
},
219219
"error_notexist": {
220220
pin: "notexist",
221-
wantErr: "'notexist' is not a valid id of a analog pin",
221+
wantErr: "'notexist' is not a valid id of an analog pin",
222222
},
223223
"error_invalid_syntax": {
224224
pin: "read/write_string",
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package adaptors
2+
3+
import (
4+
"fmt"
5+
6+
"gobot.io/x/gobot/v2/system"
7+
)
8+
9+
type AnalogPinDefinition struct {
10+
Path string
11+
R bool // readable
12+
W bool // writable
13+
BufLen uint16
14+
}
15+
16+
type AnalogPinDefinitions map[string]AnalogPinDefinition
17+
18+
type AnalogPinTranslator struct {
19+
sys *system.Accesser
20+
pinDefinitions AnalogPinDefinitions
21+
}
22+
23+
// NewAnalogPinTranslator creates a new instance of a translator for analog pins, suitable for the most cases.
24+
func NewAnalogPinTranslator(sys *system.Accesser, pinDefinitions AnalogPinDefinitions) *AnalogPinTranslator {
25+
return &AnalogPinTranslator{sys: sys, pinDefinitions: pinDefinitions}
26+
}
27+
28+
// Translate returns the sysfs path for the given id.
29+
func (pt *AnalogPinTranslator) Translate(id string) (string, bool, bool, uint16, error) {
30+
pinInfo, ok := pt.pinDefinitions[id]
31+
if !ok {
32+
return "", false, false, 0, fmt.Errorf("'%s' is not a valid id for an analog pin", id)
33+
}
34+
35+
path := pinInfo.Path
36+
info, err := pt.sys.Stat(path)
37+
if err != nil {
38+
return "", false, false, 0, fmt.Errorf("Error (%v) on access '%s'", err, path)
39+
}
40+
if info.IsDir() {
41+
return "", false, false, 0, fmt.Errorf("The item '%s' is a directory, which is not expected", path)
42+
}
43+
44+
return path, pinInfo.R, pinInfo.W, pinInfo.BufLen, nil
45+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package adaptors
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
9+
"gobot.io/x/gobot/v2/system"
10+
)
11+
12+
func TestNewAnalogPinTranslator(t *testing.T) {
13+
// arrange
14+
sys := &system.Accesser{}
15+
pinDef := AnalogPinDefinitions{}
16+
// act
17+
pt := NewAnalogPinTranslator(sys, pinDef)
18+
// assert
19+
assert.IsType(t, &AnalogPinTranslator{}, pt)
20+
assert.Equal(t, sys, pt.sys)
21+
assert.Equal(t, pinDef, pt.pinDefinitions)
22+
}
23+
24+
func TestAnalogPinTranslatorTranslate(t *testing.T) {
25+
pinDefinitions := AnalogPinDefinitions{
26+
"thermal_zone0": {Path: "/sys/class/thermal/thermal_zone0/temp", R: true, W: false, BufLen: 7},
27+
"thermal_zone1": {Path: "/sys/class/thermal/thermal_zone1/temp", R: true, W: false, BufLen: 7},
28+
}
29+
mockedPaths := []string{
30+
"/sys/class/thermal/thermal_zone0/temp",
31+
"/sys/class/thermal/thermal_zone1/temp",
32+
}
33+
tests := map[string]struct {
34+
id string
35+
wantPath string
36+
wantReadable bool
37+
wantBufLen uint16
38+
wantErr string
39+
}{
40+
"translate_thermal_zone0": {
41+
id: "thermal_zone0",
42+
wantPath: "/sys/class/thermal/thermal_zone0/temp",
43+
wantReadable: true,
44+
wantBufLen: 7,
45+
},
46+
"translate_thermal_zone1": {
47+
id: "thermal_zone1",
48+
wantPath: "/sys/class/thermal/thermal_zone1/temp",
49+
wantReadable: true,
50+
wantBufLen: 7,
51+
},
52+
"unknown_id": {
53+
id: "99",
54+
wantErr: "'99' is not a valid id for an analog pin",
55+
},
56+
}
57+
for name, tc := range tests {
58+
t.Run(name, func(t *testing.T) {
59+
// arrange
60+
sys := system.NewAccesser()
61+
_ = sys.UseMockFilesystem(mockedPaths)
62+
pt := NewAnalogPinTranslator(sys, pinDefinitions)
63+
// act
64+
path, r, w, buf, err := pt.Translate(tc.id)
65+
// assert
66+
if tc.wantErr != "" {
67+
require.EqualError(t, err, tc.wantErr)
68+
} else {
69+
require.NoError(t, err)
70+
}
71+
assert.Equal(t, tc.wantPath, path)
72+
assert.Equal(t, tc.wantReadable, r)
73+
assert.False(t, w)
74+
assert.Equal(t, tc.wantBufLen, buf)
75+
})
76+
}
77+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package adaptors
2+
3+
import "fmt"
4+
5+
type BusNumberValidator struct {
6+
validNumbers []int
7+
}
8+
9+
// NewBusNumberValidator creates a new instance for a bus number validator, used for I2C and SPI.
10+
func NewBusNumberValidator(validNumbers []int) *BusNumberValidator {
11+
return &BusNumberValidator{validNumbers: validNumbers}
12+
}
13+
14+
func (bnv *BusNumberValidator) Validate(busNr int) error {
15+
for _, validNumber := range bnv.validNumbers {
16+
if validNumber == busNr {
17+
return nil
18+
}
19+
}
20+
21+
return fmt.Errorf("Bus number %d out of range %v", busNr, bnv.validNumbers)
22+
}

0 commit comments

Comments
 (0)