|
@@ -2,237 +2,32 @@ package main
|
|
|
|
|
|
import (
|
|
import (
|
|
"flag"
|
|
"flag"
|
|
- "fmt"
|
|
|
|
"log"
|
|
"log"
|
|
"net/http"
|
|
"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() {
|
|
func main() {
|
|
var (
|
|
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.StringVar(&listen, "listen", "localhost:8080", "the HTTP listen address and port")
|
|
flag.Var(&mode, "mode", "the drive mode (default \"tank\")")
|
|
flag.Var(&mode, "mode", "the drive mode (default \"tank\")")
|
|
flag.Parse()
|
|
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 {
|
|
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.Print("HTTP server started")
|
|
log.Fatalf("error listening and serving: %v", http.ListenAndServe(listen, nil))
|
|
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}
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|