func XBoxOne(controller *usb.Device, in, out usb.Endpoint) { read := func() (tag, code byte, data []byte, err error) { var b [64]byte n, err := in.Read(b[:]) log.Printf("read %d bytes: % x [err: %v]", n, b[:n], err) if err != nil { return 0, 0, nil, err } if n < 2 { return 0, 0, nil, fmt.Errorf("only read %d bytes", n) } return b[0], b[1], b[2:n], nil } write := func(data ...byte) error { n, err := out.Write(data) log.Printf("sent %d bytes: % x [err: %v]", n, data, err) if n < len(data) { return fmt.Errorf("only sent %d of %d bytes", n, len(data)) } return err } dieIf := func(err error, format string, args ...interface{}) { if err == nil { return } msg := fmt.Sprintf(format, args...) log.Fatalf("%s: %s", msg, err) } var err error // Initializ err = write(0x05, 0x20) dieIf(err, "initialization") decode := func(data []byte) { if len(data) != 16 { log.Printf("Only got %d bytes (want 16)", len(data)) } var ( _ = data[0] // sequence number _ = data[1] // unknown btn1 = data[2] // ybxaSM?N S=share, M=Menu, N=Sync btn2 = data[3] // rlrlRLDU r=R-Stick/Trigger, l=L-Stick/Trigger, R=D-Right, L=D-Left, D=D-Down, U=D-Up lt = binary.LittleEndian.Uint16(data[4:6]) // left trigger, 0..1024 rt = binary.LittleEndian.Uint16(data[6:8]) // right trigger lx = int16(binary.LittleEndian.Uint16(data[8:10])) // left stick X, +/- 32768 or so ly = int16(binary.LittleEndian.Uint16(data[10:12])) // left stick Y rx = int16(binary.LittleEndian.Uint16(data[12:14])) // right stick X ry = int16(binary.LittleEndian.Uint16(data[14:16])) // right stick Y ) // btn1, least to most significant for i, btn := range []string{ "SYNC", "BTN1|0x02", "MENU", "SHARE", "A", "X", "B", "Y", } { if btn1&(1<<uint(i)) != 0 { log.Print(btn) } } // btn2, least to most significant for i, btn := range []string{ "D-Up", "D-Down", "D-Left", "D-Right", "L-Trigger", "R-Trigger", "L-Stick", "R-Stick", } { if btn2&(1<<uint(i)) != 0 { log.Print(btn) } } if lt > 0 || rt > 0 { log.Println("LT:", lt, "RT:", rt) } abs := func(x int16) int16 { if x < 0 { return -x } return x } if abs(lx) > 4096 || abs(ly) > 4096 || abs(rx) > 4096 || abs(ry) > 4096 { log.Println("Right:", lx, ly, "Left:", rx, ry) } } for { tag, _, data, _ := read() switch tag { case 0x07: if len(data) != 4 { log.Printf("Wanted %d bytes, got %d", 4, len(data)) break } if data[2]&0x01 != 0 { log.Print("GUIDE") } case 0x20: decode(data) } } }
func XBox360(controller *usb.Device, in, out usb.Endpoint) { // https://github.com/Grumbel/xboxdrv/blob/master/PROTOCOL const ( Empty byte = iota // 00000000 ( 0) no LEDs WarnAll // 00000001 ( 1) flash all briefly NewPlayer1 // 00000010 ( 2) p1 flash then solid NewPlayer2 // 00000011 NewPlayer3 // 00000100 NewPlayer4 // 00000101 Player1 // 00000110 ( 6) p1 solid Player2 // 00000111 Player3 // 00001000 Player4 // 00001001 Waiting // 00001010 (10) empty w/ loops WarnPlayer // 00001011 (11) flash active _ // 00001100 (12) empty Battery // 00001101 (13) squiggle Searching // 00001110 (14) slow flash Booting // 00001111 (15) solid then flash ) led := func(b byte) { out.Write([]byte{0x01, 0x03, b}) } setPlayer := func(player byte) { spin := []byte{ Player1, Player2, Player4, Player3, } spinIdx := 0 spinDelay := 100 * time.Millisecond led(Booting) time.Sleep(100 * time.Millisecond) for spinDelay > 20*time.Millisecond { led(spin[spinIdx]) time.Sleep(spinDelay) spinIdx = (spinIdx + 1) % len(spin) spinDelay -= 5 * time.Millisecond } for i := 0; i < 40; i++ { // just for safety cur := spin[spinIdx] led(cur) time.Sleep(spinDelay) spinIdx = (spinIdx + 1) % len(spin) if cur == player { break } } } led(Empty) time.Sleep(1 * time.Second) setPlayer(Player1) var b [512]byte for { n, err := in.Read(b[:]) log.Printf("read %d bytes: % x [err: %v]", n, b[:n], err) if err != nil { break } } /* time.Sleep(1 * time.Second) setPlayer(Player2) time.Sleep(1 * time.Second) setPlayer(Player3) time.Sleep(1 * time.Second) setPlayer(Player4) time.Sleep(5 * time.Second) led(Waiting) */ var last, cur [512]byte decode := func() { n, err := in.Read(cur[:]) if err != nil || n != 20 { log.Printf("ignoring read: %d bytes, err = %v", n, err) return } // 1-bit values for _, v := range []struct { idx int bit uint name string }{ {2, 0, "DPAD U"}, {2, 1, "DPAD D"}, {2, 2, "DPAD L"}, {2, 3, "DPAD R"}, {2, 4, "START"}, {2, 5, "BACK"}, {2, 6, "THUMB L"}, {2, 7, "THUMB R"}, {3, 0, "LB"}, {3, 1, "RB"}, {3, 2, "GUIDE"}, {3, 4, "A"}, {3, 5, "B"}, {3, 6, "X"}, {3, 7, "Y"}, } { c := cur[v.idx] & (1 << v.bit) l := last[v.idx] & (1 << v.bit) if c == l { continue } switch { case c != 0: log.Printf("Button %q pressed", v.name) case l != 0: log.Printf("Button %q released", v.name) } } // 8-bit values for _, v := range []struct { idx int name string }{ {4, "LT"}, {5, "RT"}, } { c := cur[v.idx] l := last[v.idx] if c == l { continue } log.Printf("Trigger %q = %v", v.name, c) } dword := func(hi, lo byte) int16 { return int16(hi)<<8 | int16(lo) } // +y // N // -x W-|-E +x // S // -y dirs := [...]string{ "W", "SW", "S", "SE", "E", "NE", "N", "NW", "W", } dir := func(x, y int16) (string, int32) { // Direction rad := math.Atan2(float64(y), float64(x)) dir := 4 * rad / math.Pi card := int(dir + math.Copysign(0.5, dir)) // Magnitude mag := math.Sqrt(float64(x)*float64(x) + float64(y)*float64(y)) return dirs[card+4], int32(mag) } // 16-bit values for _, v := range []struct { hiX, loX int hiY, loY int name string }{ {7, 6, 9, 8, "LS"}, {11, 10, 13, 12, "RS"}, } { c, cmag := dir( dword(cur[v.hiX], cur[v.loX]), dword(cur[v.hiY], cur[v.loY]), ) l, lmag := dir( dword(last[v.hiX], last[v.loX]), dword(last[v.hiY], last[v.loY]), ) ccenter := cmag < 10240 lcenter := lmag < 10240 if ccenter && lcenter { continue } if c == l && cmag == lmag { continue } if cmag > 10240 { log.Printf("Stick %q = %v x %v", v.name, c, cmag) } else { log.Printf("Stick %q centered", v.name) } } last, cur = cur, last } controller.ReadTimeout = 60 * time.Second for { decode() } }