Skip to content

Commit

Permalink
platforms/joystick: remove sdl based joystick and replace with @0xcaf…
Browse files Browse the repository at this point in the history
…ed00d package

Signed-off-by: deadprogram <ron@hybridgroup.com>
  • Loading branch information
deadprogram committed Sep 9, 2023
1 parent beaefb7 commit 603fe26
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 217 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module gobot.io/x/gobot/v2
go 1.18

require (
github.com/0xcafed00d/joystick v1.0.1
github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
github.com/eclipse/paho.mqtt.golang v1.4.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/0xcafed00d/joystick v1.0.1 h1:r4p2cRp4MHJWu1gArhGtumbkPxmr3tcOUTFqybEhplM=
github.com/0xcafed00d/joystick v1.0.1/go.mod h1:gzszjNgzP6jtCAeSdC9OqPVO5rO7TJuaw4P7eAjNzx8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/bgould/http v0.0.0-20190627042742-d268792bdee7/go.mod h1:BTqvVegvwifopl4KTEDth6Zezs9eR+lCWhvGKvkxJHE=
github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f h1:gOO/tNZMjjvTKZWpY7YnXC72ULNLErRtp94LountVE8=
Expand Down
2 changes: 1 addition & 1 deletion platforms/joystick/LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2014-2018 The Hybrid Group
Copyright (c) 2014-2023 The Hybrid Group

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
52 changes: 7 additions & 45 deletions platforms/joystick/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Joystick

You can use Gobot with any USB joystick or game controller that is compatible with [Simple DirectMedia Layer](http://www.libsdl.org/).
You can use Gobot with many USB joysticks and game controllers.

Current configurations included:

Expand All @@ -14,41 +14,24 @@ Current configurations included:

## How to Install

This package requires `sdl2` to be installed on your system
Any platform specific info here...

### macOS

To install `sdl2` on macOS using Homebrew:

```sh
brew install sdl2
```

To use an XBox360 controller on macOS, you will most likely need to install additional software such as [https://github.com/360Controller/360Controller](https://github.com/360Controller/360Controller).

### Linux (Ubuntu and Raspbian)

Please refer to the main [README.md](https://github.com/hybridgroup/gobot/blob/release/README.md)

You must be running a Linux kernel that is v4.14+ in order for the various controller mappings to work as expected.
### Windows

Then you must install the latest SDL2 v2.0.8 or greater:

```sh
wget https://www.libsdl.org/release/SDL2-2.0.8.tar.gz
tar -zxvf SDL2-2.0.8.tar.gz
cd SDL2-2.0.8/
./configure && make && sudo make install
```

## How to Use

Controller configurations are stored in Gobot it, but you can also use external file in JSON format. Take a look at the
`configs` directory for examples.
Controller configurations are stored in Gobot, but you can also use external file in JSON format. Take a look at the `configs` directory for examples.

## How to Connect

Plug your USB joystick or game controller into your USB port. If your device is supported by SDL, you are now ready.
Plug your USB joystick or game controller into your USB port. If your device is supported by your operating system, it might prompt you to install some system drivers.

For the Dualshock4, you must pair the device with your computers Bluetooth interface first, before running your Gobot program.

Expand All @@ -67,7 +50,7 @@ import (
)

func main() {
joystickAdaptor := joystick.NewAdaptor()
joystickAdaptor := joystick.NewAdaptor(0)
stick := joystick.NewDriver(joystickAdaptor, "dualshock3",
)

Expand Down Expand Up @@ -151,25 +134,4 @@ func main() {

## How to Add A New Joystick

In the `bin` directory for this package is a CLI utility program that scans for SDL joystick events, and displays the ID
and value:

```sh
$ go run ./platforms/joystick/bin/scanner.go
Joystick 0 connected
[6625 ms] Axis: 1 value:-22686
[6641 ms] Axis: 1 value:-32768
[6836 ms] Axis: 1 value:-18317
[6852 ms] Axis: 1 value:0
[8663 ms] Axis: 3 value:-32768
[8873 ms] Axis: 3 value:0
[10183 ms] Axis: 0 value:-24703
[10183 ms] Axis: 0 value:-32768
[10313 ms] Axis: 1 value:-3193
[10329 ms] Axis: 1 value:0
[10345 ms] Axis: 0 value:0
```

You can use the output from this program to create a JSON file for the various buttons and axes on your joystick/gamepad.
You could also create a file similar to `joystick_dualshock3.go` and submit a pull request with the new configuration so
others can use it as well.
You can create a file similar to `joystick_dualshock3.go` and submit a pull request with the new configuration so others can use it as well.
12 changes: 10 additions & 2 deletions platforms/joystick/configs/dualshock3.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,20 @@
"id": 1
},
{
"name": "right_x",
"name": "l2",
"id": 2
},
{
"name": "right_y",
"name": "right_x",
"id": 3
},
{
"name": "right_y",
"id": 4
},
{
"name": "r2",
"id": 5
}
],
"buttons": [
Expand Down
2 changes: 1 addition & 1 deletion platforms/joystick/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Example:
)
func main() {
joystickAdaptor := joystick.NewAdaptor()
joystickAdaptor := joystick.NewAdaptor(0)
joystick := joystick.NewDriver(joystickAdaptor,
"./platforms/joystick/configs/dualshock3.json",
)
Expand Down
26 changes: 14 additions & 12 deletions platforms/joystick/joystick_adaptor.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
package joystick

import (
"errors"
"fmt"

"gobot.io/x/gobot/v2"

"github.com/veandco/go-sdl2/sdl"
js "github.com/0xcafed00d/joystick"
)

type joystick interface {
Close()
InstanceID() sdl.JoystickID
ID() int
}

// Adaptor represents a connection to a joystick
type Adaptor struct {
name string
joystick joystick
id int
joystick js.Joystick
connect func(*Adaptor) error
}

// NewAdaptor returns a new Joystick Adaptor.
func NewAdaptor() *Adaptor {
// Pass in the ID of the joystick you wish to connect to.
func NewAdaptor(id int) *Adaptor {
return &Adaptor{
name: gobot.DefaultName("Joystick"),
connect: func(j *Adaptor) error {
if err := sdl.Init(sdl.INIT_JOYSTICK); err != nil {
return err
joy, err := js.Open(id)
if err != nil {
return fmt.Errorf("No joystick available: %v", err)
}
if sdl.NumJoysticks() > 0 {
j.joystick = sdl.JoystickOpen(0)
return nil
}
return errors.New("No joystick available")

j.id = id
j.joystick = joy
return nil
},
}
}
Expand Down
8 changes: 4 additions & 4 deletions platforms/joystick/joystick_adaptor_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package joystick

import (
"errors"
"strings"
"testing"

Expand All @@ -12,7 +11,7 @@ import (
var _ gobot.Adaptor = (*Adaptor)(nil)

func initTestAdaptor() *Adaptor {
a := NewAdaptor()
a := NewAdaptor(6)
a.connect = func(j *Adaptor) (err error) {
j.joystick = &testJoystick{}
return nil
Expand All @@ -31,8 +30,9 @@ func TestAdaptorConnect(t *testing.T) {
a := initTestAdaptor()
gobottest.Assert(t, a.Connect(), nil)

a = NewAdaptor()
gobottest.Assert(t, a.Connect(), errors.New("No joystick available"))
a = NewAdaptor(6)
err := a.Connect()
gobottest.Assert(t, strings.HasPrefix(err.Error(), "No joystick available"), true)
}

func TestAdaptorFinalize(t *testing.T) {
Expand Down
103 changes: 48 additions & 55 deletions platforms/joystick/joystick_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"os"
"time"

"github.com/veandco/go-sdl2/sdl"
"gobot.io/x/gobot/v2"
js "github.com/0xcafed00d/joystick"
)

const (
Expand Down Expand Up @@ -46,7 +46,9 @@ type Driver struct {
connection gobot.Connection
configPath string
config joystickConfig
poll func() sdl.Event
buttonState map[int]bool
axisState map[int]int

halt chan bool
gobot.Eventer
}
Expand Down Expand Up @@ -86,9 +88,9 @@ func NewDriver(a *Adaptor, config string, v ...time.Duration) *Driver {
connection: a,
Eventer: gobot.NewEventer(),
configPath: config,
poll: func() sdl.Event {
return sdl.PollEvent()
},
buttonState: make(map[int]bool),
axisState: make(map[int]int),

interval: 10 * time.Millisecond,
halt: make(chan bool),
}
Expand Down Expand Up @@ -159,18 +161,17 @@ func (j *Driver) Start() (err error) {
for _, value := range j.config.Axis {
j.AddEvent(value.Name)
}
for _, value := range j.config.Hats {
j.AddEvent(fmt.Sprintf("%s_press", value.Name))
j.AddEvent(fmt.Sprintf("%s_release", value.Name))
}

go func() {
for {
for event := j.poll(); event != nil; event = j.poll() {
if errs := j.handleEvent(event); errs != nil {
j.Publish(j.Event("error"), errs)
}
state, err := j.adaptor().joystick.Read()
if err != nil {
j.Publish(j.Event("error"), err)
break
}
j.handleButtons(state)
j.handleAxes(state)

select {
case <-time.After(j.interval):
case <-j.halt:
Expand All @@ -189,46 +190,48 @@ func (j *Driver) Halt() (err error) {

var previousHat = ""

// HandleEvent publishes an specific event according to data received
func (j *Driver) handleEvent(event sdl.Event) error {
switch data := event.(type) {
case *sdl.JoyAxisEvent:
if data.Which == j.adaptor().joystick.InstanceID() {
axis := j.findName(data.Axis, j.config.Axis)
if axis == "" {
return fmt.Errorf("Unknown Axis: %v", data.Axis)
func (j *Driver) handleButtons(state js.State) error {
for button := 0; button < j.adaptor().joystick.ButtonCount(); button++ {
switch {
case state.Buttons&(1<<uint32(button)) != 0 && !j.buttonState[button]:
j.buttonState[button] = true
name := j.findName(uint8(button), j.config.Buttons)
if name == "" {
return fmt.Errorf("Unknown button: %v", button)
}
j.Publish(j.Event(axis), data.Value)
}
case *sdl.JoyButtonEvent:
if data.Which == j.adaptor().joystick.InstanceID() {
button := j.findName(data.Button, j.config.Buttons)
if button == "" {
return fmt.Errorf("Unknown Button: %v", data.Button)
}
if data.State == 1 {
j.Publish(j.Event(fmt.Sprintf("%s_press", button)), nil)
} else {
j.Publish(j.Event(fmt.Sprintf("%s_release", button)), nil)

j.Publish(j.Event(fmt.Sprintf("%s_press", name)), nil)
case state.Buttons&(1<<uint32(button)) == 0 && j.buttonState[button]:
j.buttonState[button] = false
name := j.findName(uint8(button), j.config.Buttons)
if name == "" {
return fmt.Errorf("Unknown button: %v", button)
}

j.Publish(j.Event(fmt.Sprintf("%s_release", name)), nil)
}
case *sdl.JoyHatEvent:
if data.Which == j.adaptor().joystick.InstanceID() {
hat := j.findHatName(data.Value, data.Hat, j.config.Hats)
if hat == "" {
return fmt.Errorf("Unknown Hat: %v %v", data.Hat, data.Value)
} else if hat == "released" {
hat = previousHat
j.Publish(j.Event(fmt.Sprintf("%s_release", hat)), true)
} else {
previousHat = hat
j.Publish(j.Event(fmt.Sprintf("%s_press", hat)), true)
}
}

return nil
}

func (j *Driver) handleAxes(state js.State) error {
for axis := 0; axis < j.adaptor().joystick.AxisCount(); axis++ {
name := j.findName(uint8(axis), j.config.Axis)
if name == "" {
return fmt.Errorf("Unknown Axis: %v", axis)
}

if j.axisState[axis] != state.AxisData[axis] {
j.axisState[axis] = state.AxisData[axis]
j.Publish(name, state.AxisData[axis])
}
}

return nil
}

// findName returns name from button or axis found by id in provided list
func (j *Driver) findName(id uint8, list []pair) string {
for _, value := range list {
if int(id) == value.ID {
Expand All @@ -238,16 +241,6 @@ func (j *Driver) findName(id uint8, list []pair) string {
return ""
}

// findHatName returns name from hat found by id in provided list
func (j *Driver) findHatName(id uint8, hat uint8, list []hat) string {
for _, lHat := range list {
if int(id) == lHat.ID && int(hat) == lHat.Hat {
return lHat.Name
}
}
return ""
}

// loadFile load the joystick config from a .json file
func (j *Driver) loadFile() error {
file, e := os.ReadFile(j.configPath)
Expand Down
Loading

0 comments on commit 603fe26

Please sign in to comment.