Browse Source

Refactor gamepad to its own package

Douglas Thrift 1 year ago
parent
commit
c35591e356
4 changed files with 354 additions and 216 deletions
  1. 2 2
      README.md
  2. 266 0
      gamepad/gamepad.go
  3. 77 0
      rover/control.go
  4. 9 214
      rover/main.go

+ 2 - 2
README.md

@@ -1,3 +1,3 @@
-# dt-robots
+# DT Robots
 
-Robots
+Robots

+ 266 - 0
gamepad/gamepad.go

@@ -0,0 +1,266 @@
+package gamepad
+
+import (
+	"fmt"
+	"log"
+
+	"github.com/gvalkov/golang-evdev"
+)
+
+// event types
+const (
+	Button = iota
+	AxisX
+	AxisY
+	AxisZ
+)
+
+type Event struct {
+	Type  int
+	Value int32
+}
+
+func (e Event) IsAxis() bool {
+	switch e.Type {
+	case AxisX, AxisY, AxisZ:
+		return true
+	default:
+		return false
+	}
+}
+
+func (e Event) IsAxisX() bool {
+	return e.Type == AxisX
+}
+
+func (e Event) IsAxisY() bool {
+	return e.Type == AxisY
+}
+
+func (e Event) IsAxisZ() bool {
+	return e.Type == AxisZ
+}
+
+func (e Event) IsButton() bool {
+	return e.Type == Button
+}
+
+func (e Event) IsButtonPress() bool {
+	if e.IsButton() && e.Value > 0 {
+		return true
+	}
+	return false
+}
+
+func (e Event) String() string {
+	var code string
+	switch e.Type {
+	case Button:
+		code = "button"
+	case AxisX:
+		code = "X"
+	case AxisY:
+		code = "Y"
+	case AxisZ:
+		code = "Z"
+	}
+	return fmt.Sprintf("%v %v", code, e.Value)
+}
+
+// Logitech gamepad button channels
+const (
+	LeftAnalog  = 0x001
+	RightAnalog = 0x002
+	DPad        = 0x004
+	A           = 0x008
+	B           = 0x010
+	X           = 0x020
+	Y           = 0x040
+	Left        = 0x080
+	Right       = 0x100
+	Back        = 0x200
+	Start       = 0x400
+	Logitech    = 0x800
+	All         = 0xfff
+)
+
+const channelBufferSize = 16
+
+type Gamepad struct {
+	channels                      int16
+	device                        *evdev.InputDevice
+	LeftAnalog, RightAnalog, DPad chan Event
+	A, B, X, Y                    chan Event
+	Left, Right                   chan Event
+	Back, Start, Logitech         chan Event
+}
+
+func NewGamepad(device string, channels int16) (*Gamepad, error) {
+	g := &Gamepad{channels: channels}
+
+	if !evdev.IsInputDevice(device) {
+		return nil, fmt.Errorf("%v is not an input device", device)
+	}
+	var err error
+	g.device, err = evdev.Open(device)
+	if err != nil {
+		return nil, err
+	}
+
+	if channels&LeftAnalog != 0 {
+		g.LeftAnalog = make(chan Event, channelBufferSize)
+	}
+	if channels&RightAnalog != 0 {
+		g.RightAnalog = make(chan Event, channelBufferSize)
+	}
+	if channels&DPad != 0 {
+		g.DPad = make(chan Event, channelBufferSize)
+	}
+	if channels&A != 0 {
+		g.A = make(chan Event, channelBufferSize)
+	}
+	if channels&B != 0 {
+		g.B = make(chan Event, channelBufferSize)
+	}
+	if channels&X != 0 {
+		g.X = make(chan Event, channelBufferSize)
+	}
+	if channels&Y != 0 {
+		g.Y = make(chan Event, channelBufferSize)
+	}
+	if channels&Left != 0 {
+		g.Left = make(chan Event, channelBufferSize)
+	}
+	if channels&Right != 0 {
+		g.Right = make(chan Event, channelBufferSize)
+	}
+	if channels&Back != 0 {
+		g.Back = make(chan Event, channelBufferSize)
+	}
+	if channels&Start != 0 {
+		g.Start = make(chan Event, channelBufferSize)
+	}
+	if channels&Logitech != 0 {
+		g.Logitech = make(chan Event, channelBufferSize)
+	}
+
+	return g, nil
+}
+
+// Logitech gamepad codes
+const (
+	leftAnalogX  = 0
+	leftAnalogY  = 1
+	leftAnalogZ  = 2
+	rightAnalogX = 3
+	rightAnalogY = 4
+	rightAnalogZ = 5
+	dPadX        = 16
+	dPadY        = 17
+	a            = 304
+	b            = 305
+	x            = 307
+	y            = 308
+	left         = 310
+	right        = 311
+	back         = 314
+	start        = 315
+	logitech     = 316
+	leftAnalog   = 317
+	rightAnalog  = 318
+)
+
+func (g *Gamepad) Read() error {
+	log.Print("gamepad read loop started")
+	for {
+		e, err := g.device.ReadOne()
+		if err != nil {
+			return err
+		}
+
+		switch e.Type {
+		case evdev.EV_ABS:
+			switch e.Code {
+			case leftAnalogX:
+				if g.channels&LeftAnalog != 0 {
+					g.LeftAnalog <- Event{Type: AxisX, Value: e.Value}
+				}
+			case leftAnalogY:
+				if g.channels&LeftAnalog != 0 {
+					g.LeftAnalog <- Event{Type: AxisY, Value: e.Value}
+				}
+			case leftAnalogZ:
+				if g.channels&LeftAnalog != 0 {
+					g.LeftAnalog <- Event{Type: AxisZ, Value: e.Value}
+				}
+			case rightAnalogX:
+				if g.channels&RightAnalog != 0 {
+					g.RightAnalog <- Event{Type: AxisX, Value: e.Value}
+				}
+			case rightAnalogY:
+				if g.channels&RightAnalog != 0 {
+					g.RightAnalog <- Event{Type: AxisY, Value: e.Value}
+				}
+			case rightAnalogZ:
+				if g.channels&RightAnalog != 0 {
+					g.RightAnalog <- Event{Type: AxisZ, Value: e.Value}
+				}
+			case dPadX:
+				if g.channels&DPad != 0 {
+					g.DPad <- Event{Type: AxisX, Value: e.Value}
+				}
+			case dPadY:
+				if g.channels&DPad != 0 {
+					g.DPad <- Event{Type: AxisY, Value: e.Value}
+				}
+			}
+		case evdev.EV_KEY:
+			switch e.Code {
+			case a:
+				if g.channels&A != 0 {
+					g.A <- Event{Type: Button, Value: e.Value}
+				}
+			case b:
+				if g.channels&B != 0 {
+					g.B <- Event{Type: Button, Value: e.Value}
+				}
+			case x:
+				if g.channels&X != 0 {
+					g.X <- Event{Type: Button, Value: e.Value}
+				}
+			case y:
+				if g.channels&Y != 0 {
+					g.Y <- Event{Type: Button, Value: e.Value}
+				}
+			case left:
+				if g.channels&Left != 0 {
+					g.Left <- Event{Type: Button, Value: e.Value}
+				}
+			case right:
+				if g.channels&Right != 0 {
+					g.Right <- Event{Type: Button, Value: e.Value}
+				}
+			case back:
+				if g.channels&Back != 0 {
+					g.Back <- Event{Type: Button, Value: e.Value}
+				}
+			case start:
+				if g.channels&Start != 0 {
+					g.Start <- Event{Type: Button, Value: e.Value}
+				}
+			case logitech:
+				if g.channels&Logitech != 0 {
+					g.Logitech <- Event{Type: Button, Value: e.Value}
+				}
+			case leftAnalog:
+				if g.channels&LeftAnalog != 0 {
+					g.LeftAnalog <- Event{Type: Button, Value: e.Value}
+				}
+			case rightAnalog:
+				if g.channels&LeftAnalog != 0 {
+					g.RightAnalog <- Event{Type: Button, Value: e.Value}
+				}
+			}
+		}
+	}
+}

+ 77 - 0
rover/control.go

@@ -0,0 +1,77 @@
+package main
+
+import (
+	"fmt"
+	"log"
+
+	"douglasthrift.net/dtrobots/gamepad"
+)
+
+// drive modes
+const (
+	tank = iota
+	joystick
+)
+
+const driveModeCount = 2
+
+type driveMode int
+
+func (d driveMode) String() string {
+	switch d {
+	case tank:
+		return "tank"
+	case joystick:
+		return "joystick"
+	default:
+		return "unknown"
+	}
+}
+
+func (d *driveMode) Set(value string) error {
+	switch value {
+	case "tank":
+		*d = tank
+	case "joystick":
+		*d = joystick
+	default:
+		return fmt.Errorf("unknown drive mode: %v", value)
+	}
+	return nil
+}
+
+func control(mode driveMode, g *gamepad.Gamepad) {
+	log.Printf("control loop started (drive mode: %v)", mode)
+	for {
+		select {
+		case e := <-g.LeftAnalog:
+			log.Println("left analog", e)
+		case e := <-g.RightAnalog:
+			log.Println("right analog", e)
+		case e := <-g.DPad:
+			log.Println("dpad", e)
+		case e := <-g.A:
+			log.Println("a", e)
+		case e := <-g.B:
+			log.Println("b", e)
+		case e := <-g.X:
+			log.Println("x", e)
+		case e := <-g.Y:
+			log.Println("y", e)
+		case e := <-g.Left:
+			log.Println("left", e)
+		case e := <-g.Right:
+			log.Println("right", e)
+		case e := <-g.Back:
+			log.Println("back", e)
+		case e := <-g.Start:
+			log.Println("start", e)
+		case e := <-g.Logitech:
+			if e.IsButtonPress() {
+				mode++
+				mode %= driveModeCount
+				log.Printf("switched drive mode: %v", mode)
+			}
+		}
+	}
+}

+ 9 - 214
rover/main.go

@@ -2,237 +2,32 @@ package main
 
 import (
 	"flag"
-	"fmt"
 	"log"
 	"net/http"
 
-	"github.com/gvalkov/golang-evdev"
+	"douglasthrift.net/dtrobots/gamepad"
 )
 
-const (
-	// Logitech gamepad codes
-	codeLeftAnalogX  = 0
-	codeLeftAnalogY  = 1
-	codeLeftAnalogZ  = 2
-	codeRightAnalogX = 3
-	codeRightAnalogY = 4
-	codeRightAnalogZ = 5
-	codeDPadX        = 16
-	codeDPadY        = 17
-	codeA            = 304
-	codeB            = 305
-	codeX            = 307
-	codeY            = 308
-	codeLeft         = 310
-	codeRight        = 311
-	codeBack         = 314
-	codeStart        = 315
-	codeLogitech     = 316
-	codeLeftAnalog   = 317
-	codeRightAnalog  = 318
-)
-
-const (
-	// event types
-	button = iota
-	axisX
-	axisY
-	axisZ
-)
-
-const gamepadChanBufSize = 16
-
-type event struct {
-	code  int
-	value int32
-}
-
-func (e event) String() string {
-	var code string
-	switch e.code {
-	case button:
-		code = "button"
-	case axisX:
-		code = "X"
-	case axisY:
-		code = "Y"
-	case axisZ:
-		code = "Z"
-	}
-	return fmt.Sprintf("%v %v", code, e.value)
-}
-
-type gamepadEvents struct {
-	leftAnalog, rightAnalog, dpad chan event
-	a, b, x, y                    chan event
-	left, right                   chan event
-	back, start, logitech         chan event
-}
-
-const (
-	// drive modes
-	tank = iota
-	joystick
-)
-
-const driveModeCount = 2
-
-type driveMode int
-
-func (d driveMode) String() string {
-	switch d {
-	case tank:
-		return "tank"
-	case joystick:
-		return "joystick"
-	default:
-		return "unknown"
-	}
-}
-
-func (d *driveMode) Set(value string) error {
-	switch value {
-	case "tank":
-		*d = tank
-	case "joystick":
-		*d = joystick
-	default:
-		return fmt.Errorf("unknown drive mode: %v", value)
-	}
-	return nil
-}
-
 func main() {
 	var (
-		gamepad string
-		listen  string
-		mode    driveMode
+		device string
+		listen string
+		mode   driveMode
 	)
 
-	flag.StringVar(&gamepad, "gamepad", "/dev/input/event0", "the gamepad device path")
+	flag.StringVar(&device, "gamepad", "/dev/input/event0", "the gamepad device path")
 	flag.StringVar(&listen, "listen", "localhost:8080", "the HTTP listen address and port")
 	flag.Var(&mode, "mode", "the drive mode (default \"tank\")")
 	flag.Parse()
 
-	if !evdev.IsInputDevice(gamepad) {
-		log.Fatalf("%v is not an input device", gamepad)
-	}
-	device, err := evdev.Open(gamepad)
+	g, err := gamepad.NewGamepad(device, gamepad.All)
 	if err != nil {
-		log.Fatalf("error opening gamepad: %v", err)
-	}
-
-	events := &gamepadEvents{
-		leftAnalog:  make(chan event, gamepadChanBufSize),
-		rightAnalog: make(chan event, gamepadChanBufSize),
-		dpad:        make(chan event, gamepadChanBufSize),
-		a:           make(chan event, gamepadChanBufSize),
-		b:           make(chan event, gamepadChanBufSize),
-		x:           make(chan event, gamepadChanBufSize),
-		y:           make(chan event, gamepadChanBufSize),
-		left:        make(chan event, gamepadChanBufSize),
-		right:       make(chan event, gamepadChanBufSize),
-		back:        make(chan event, gamepadChanBufSize),
-		start:       make(chan event, gamepadChanBufSize),
-		logitech:    make(chan event, gamepadChanBufSize),
+		log.Fatalf("error creating gamepad: %v", err)
 	}
 
-	go control(mode, events)
-	go gamepadRead(device, events)
+	go control(mode, g)
+	go log.Fatalf("error reading from gamepad: %v", g.Read())
 
 	log.Print("HTTP server started")
 	log.Fatalf("error listening and serving: %v", http.ListenAndServe(listen, nil))
 }
-
-func control(mode driveMode, events *gamepadEvents) {
-	log.Printf("control loop started (drive mode: %v)", mode)
-	for {
-		select {
-		case e := <-events.leftAnalog:
-			log.Println("left analog", e)
-		case e := <-events.rightAnalog:
-			log.Println("right analog", e)
-		case e := <-events.dpad:
-			log.Println("dpad", e)
-		case e := <-events.a:
-			log.Println("a", e)
-		case e := <-events.b:
-			log.Println("b", e)
-		case e := <-events.x:
-			log.Println("x", e)
-		case e := <-events.y:
-			log.Println("y", e)
-		case e := <-events.left:
-			log.Println("left", e)
-		case e := <-events.right:
-			log.Println("right", e)
-		case e := <-events.back:
-			log.Println("back", e)
-		case e := <-events.start:
-			log.Println("start", e)
-		case e := <-events.logitech:
-			if e.value > 0 {
-				mode++
-				mode %= driveModeCount
-				log.Printf("switched drive mode: %v", mode)
-			}
-		}
-	}
-}
-
-func gamepadRead(device *evdev.InputDevice, events *gamepadEvents) {
-	log.Print("gamepad read loop started")
-	for {
-		e, err := device.ReadOne()
-		if err != nil {
-			log.Fatalf("error reading from gamepad: %v", err)
-		}
-
-		switch e.Type {
-		case evdev.EV_ABS:
-			switch e.Code {
-			case codeLeftAnalogX:
-				events.leftAnalog <- event{code: axisX, value: e.Value}
-			case codeLeftAnalogY:
-				events.leftAnalog <- event{code: axisY, value: e.Value}
-			case codeLeftAnalogZ:
-				events.leftAnalog <- event{code: axisZ, value: e.Value}
-			case codeRightAnalogX:
-				events.rightAnalog <- event{code: axisX, value: e.Value}
-			case codeRightAnalogY:
-				events.rightAnalog <- event{code: axisY, value: e.Value}
-			case codeRightAnalogZ:
-				events.rightAnalog <- event{code: axisZ, value: e.Value}
-			case codeDPadX:
-				events.dpad <- event{code: axisX, value: e.Value}
-			case codeDPadY:
-				events.dpad <- event{code: axisY, value: e.Value}
-			}
-		case evdev.EV_KEY:
-			switch e.Code {
-			case codeA:
-				events.a <- event{code: button, value: e.Value}
-			case codeB:
-				events.b <- event{code: button, value: e.Value}
-			case codeX:
-				events.x <- event{code: button, value: e.Value}
-			case codeY:
-				events.y <- event{code: button, value: e.Value}
-			case codeLeft:
-				events.left <- event{code: button, value: e.Value}
-			case codeRight:
-				events.right <- event{code: button, value: e.Value}
-			case codeBack:
-				events.back <- event{code: button, value: e.Value}
-			case codeStart:
-				events.start <- event{code: button, value: e.Value}
-			case codeLogitech:
-				events.logitech <- event{code: button, value: e.Value}
-			case codeLeftAnalog:
-				events.leftAnalog <- event{code: button, value: e.Value}
-			case codeRightAnalog:
-				events.rightAnalog <- event{code: button, value: e.Value}
-			}
-		}
-	}
-}