├── .gitignore ├── LICENSE ├── README.md ├── connection.go ├── controller.go ├── emitter.go ├── event.go ├── examples └── demo │ └── main.go ├── go.mod ├── go.sum ├── hid └── device.go ├── led └── led.go ├── rumble └── rumble.go └── state.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Christopher Rosell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gods4 2 | 3 | A userspace cross-platform driver for Sony DualShock 4 controller over HID. 4 | Works for Bluetooth and USB connections. 5 | 6 | ## Features 7 | 8 | * Buttons: x, circle, square, triangle, share, options, touchpad 9 | * D-pad: up, down, left, right (also intermediate positions) 10 | * Analog sticks: left, right 11 | * Analog triggers: L2, R2 12 | * Touchpad: 2 touches and button 13 | * Battery 14 | * Gyroscope (absolute orientation) 15 | * Accelerometer (relative movement) 16 | * Activating the motors (rumble) 17 | * Setting the LED color 18 | 19 | ## Install 20 | 21 | Use go get to install the latest version of the library: 22 | 23 | go get github.com/kpeu3i/gods4@v1.0.0 24 | 25 | Next, include ds4 in your application: 26 | 27 | ```go 28 | import "github.com/kpeu3i/gods4" 29 | ``` 30 | 31 | ## Usage 32 | 33 | First, connect the controller to your computer using a micro-USB cable or Bluetooth. 34 | 35 | ```go 36 | package main 37 | 38 | import ( 39 | "log" 40 | "os" 41 | "os/signal" 42 | "syscall" 43 | 44 | "github.com/kpeu3i/gods4" 45 | "github.com/kpeu3i/gods4/led" 46 | "github.com/kpeu3i/gods4/rumble" 47 | ) 48 | 49 | func main() { 50 | // Find all controllers connected to your machine via USB or Bluetooth 51 | controllers := gods4.Find() 52 | if len(controllers) == 0 { 53 | panic("No connected DS4 controllers found") 54 | } 55 | 56 | // Select first controller from the list 57 | controller := controllers[0] 58 | 59 | // Connect to the controller 60 | err := controller.Connect() 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | log.Printf("* Controller #1 | %-10s | name: %s, connection: %s\n", "Connect", controller, controller.ConnectionType()) 66 | 67 | // Disconnect controller when a program is terminated 68 | signals := make(chan os.Signal, 1) 69 | signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) 70 | go func() { 71 | <-signals 72 | err := controller.Disconnect() 73 | if err != nil { 74 | panic(err) 75 | } 76 | log.Printf("* Controller #1 | %-10s | bye!\n", "Disconnect") 77 | }() 78 | 79 | // Register callback for "BatteryUpdate" event 80 | controller.On(gods4.EventBatteryUpdate, func(data interface{}) error { 81 | battery := data.(gods4.Battery) 82 | log.Printf("* Controller #1 | %-10s | capacity: %v%%, charging: %v, cable: %v\n", 83 | "Battery", 84 | battery.Capacity, 85 | battery.IsCharging, 86 | battery.IsCableConnected, 87 | ) 88 | 89 | return nil 90 | }) 91 | 92 | // Register callback for "CrossPress" event 93 | controller.On(gods4.EventCrossPress, func(data interface{}) error { 94 | log.Printf("* Controller #1 | %-10s | state: press\n", "Cross") 95 | 96 | return nil 97 | }) 98 | 99 | // Register callback for "CrossRelease" event 100 | controller.On(gods4.EventCrossRelease, func(data interface{}) error { 101 | log.Printf("* Controller #1 | %-10s | state: release\n", "Cross") 102 | 103 | return nil 104 | }) 105 | 106 | // Register callback for "RightStickMove" event 107 | controller.On(gods4.EventRightStickMove, func(data interface{}) error { 108 | stick := data.(gods4.Stick) 109 | log.Printf("* Controller #1 | %-10s | x: %v, y: %v\n", "RightStick", stick.X, stick.Y) 110 | 111 | return nil 112 | }) 113 | 114 | // Enable left and right rumble motors 115 | err = controller.Rumble(rumble.Both()) 116 | if err != nil { 117 | panic(err) 118 | } 119 | 120 | // Enable LED (yellow) with flash 121 | err = controller.Led(led.Yellow().Flash(50, 50)) 122 | if err != nil { 123 | panic(err) 124 | } 125 | 126 | // Start listening for controller events 127 | err = controller.Listen() 128 | if err != nil { 129 | panic(err) 130 | } 131 | 132 | // Output: 133 | // 2019/02/16 17:00:23 * Controller #1 | Connect | name: Wireless Controller (vendor: 1356, product: 2508), connection: BT 134 | // 2019/02/16 17:00:23 * Controller #1 | Battery | capacity: 77%, charging: false, cable: false 135 | // 2019/02/16 17:00:34 * Controller #1 | Cross | state: press 136 | // 2019/02/16 17:00:34 * Controller #1 | Cross | state: release 137 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 187, y: 98 138 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 191, y: 94 139 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 196, y: 93 140 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 212, y: 88 141 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 228, y: 79 142 | // 2019/02/16 17:02:52 * Controller #1 | Disconnect | bye! 143 | } 144 | ``` 145 | 146 | 147 | ## Events 148 | 149 | Events on which you can subscribe are listed below: 150 | 151 | Name | Data 152 | --- | --- 153 | EventCrossPress | nil 154 | EventCrossRelease | nil 155 | EventCirclePress | nil 156 | EventCircleRelease | nil 157 | EventSquarePress | nil 158 | EventSquareRelease | nil 159 | EventTrianglePress | nil 160 | EventTriangleRelease | nil 161 | EventL1Press | nil 162 | EventL1Release | nil 163 | EventL2Press | byte 164 | EventL2Release | byte 165 | EventL3Press | nil 166 | EventL3Release | nil 167 | EventR1Press | nil 168 | EventR1Release | nil 169 | EventR2Press | byte 170 | EventR2Release | byte 171 | EventR3Press | nil 172 | EventR3Release | nil 173 | EventDPadUpPress | nil 174 | EventDPadUpRelease | nil 175 | EventDPadDownPress | nil 176 | EventDPadDownRelease | nil 177 | EventDPadLeftPress | nil 178 | EventDPadLeftRelease | nil 179 | EventDPadRightPress | nil 180 | EventDPadRightRelease | nil 181 | EventSharePress | nil 182 | EventShareRelease | nil 183 | EventOptionsPress | nil 184 | EventOptionsRelease | nil 185 | EventTouchpadSwipe | Touchpad 186 | EventTouchpadPress | Touchpad 187 | EventTouchpadRelease | Touchpad 188 | EventPSPress | nil 189 | EventPSRelease | nil 190 | EventLeftStickMove | Stick 191 | EventRightStickMove | Stick 192 | EventAccelerometerUpdate | Accelerometer 193 | EventGyroscopeUpdate | Gyroscope 194 | EventBatteryUpdate | Battery 195 | 196 | ## TODO 197 | 198 | * Microphone/speaker 199 | * Tests 200 | 201 | ## References 202 | 203 | The DualShock 4 report format is not open and these resources have been very helpful when creating gods4: 204 | 205 | * https://github.com/seidtgeist/node-ds4 206 | * https://github.com/ehd/node-ds4 207 | * https://github.com/chrippa/ds4drv 208 | * http://www.psdevwiki.com/ps4/DualShock_4 209 | * http://eleccelerator.com/wiki/index.php?title=DualShock_4 210 | -------------------------------------------------------------------------------- /connection.go: -------------------------------------------------------------------------------- 1 | package gods4 2 | 3 | type ConnectionType uint 4 | 5 | const ( 6 | ConnectionTypeNone ConnectionType = iota 7 | ConnectionTypeUSB 8 | ConnectionTypeBluetooth 9 | ) 10 | 11 | func (t ConnectionType) String() string { 12 | switch t { 13 | case ConnectionTypeNone: 14 | return "NONE" 15 | case ConnectionTypeUSB: 16 | return "USB" 17 | case ConnectionTypeBluetooth: 18 | return "BT" 19 | default: 20 | return "" 21 | } 22 | } 23 | 24 | func detectConnectionType(device Device) (ConnectionType, error) { 25 | _, _ = device.GetFeatureReport(getFeatureReportCode0x04) 26 | 27 | bytes := make([]byte, 2) 28 | for i := 1; i <= 100; i++ { 29 | _, err := device.Read(bytes) 30 | if err != nil { 31 | return 0, ErrInvalidConnectionType 32 | } 33 | } 34 | 35 | if bytes[0] == 0x11 && bytes[1] == 0xC0 { 36 | return ConnectionTypeBluetooth, nil 37 | } 38 | 39 | return ConnectionTypeUSB, nil 40 | } 41 | -------------------------------------------------------------------------------- /controller.go: -------------------------------------------------------------------------------- 1 | package gods4 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "hash/crc32" 7 | "sync" 8 | 9 | "github.com/pkg/errors" 10 | 11 | "github.com/kpeu3i/gods4/hid" 12 | "github.com/kpeu3i/gods4/led" 13 | "github.com/kpeu3i/gods4/rumble" 14 | ) 15 | 16 | var ( 17 | ErrInvalidConnectionType = errors.New("ds4: can't detect connection type") 18 | ErrControllerIsConnected = errors.New("ds4: controller is already connected") 19 | ErrControllerIsNotConnected = errors.New("ds4: controller is not connected") 20 | ErrControllerIsListening = errors.New("ds4: controller is already listening for events") 21 | ) 22 | 23 | const getFeatureReportCode0x04 = 0x04 24 | 25 | type Device interface { 26 | VendorID() uint16 27 | ProductID() uint16 28 | Path() string 29 | Release() uint16 30 | Serial() string 31 | Manufacturer() string 32 | Product() string 33 | Open() error 34 | Close() error 35 | Read(b []byte) (int, error) 36 | Write(b []byte) (int, error) 37 | GetFeatureReport(code byte) ([]byte, error) 38 | } 39 | 40 | type Controller struct { 41 | mutex sync.RWMutex 42 | device Device 43 | connectionType ConnectionType 44 | emitter *emitter 45 | inputOffset uint 46 | inputCurrState *state 47 | inputPrevState *state 48 | outputOffset uint 49 | outputState []byte 50 | isListening bool 51 | errors chan error 52 | quit chan struct{} 53 | } 54 | 55 | type Callback func(data interface{}) error 56 | 57 | func NewController(device Device) *Controller { 58 | return &Controller{ 59 | device: device, 60 | connectionType: ConnectionTypeNone, 61 | emitter: newEmitter(), 62 | errors: make(chan error), 63 | quit: make(chan struct{}), 64 | } 65 | } 66 | 67 | func (c *Controller) Connect() error { 68 | c.mutex.Lock() 69 | defer c.mutex.Unlock() 70 | 71 | err := c.errorIfConnected() 72 | if err != nil { 73 | return err 74 | } 75 | 76 | err = c.device.Open() 77 | if err != nil { 78 | return err 79 | } 80 | 81 | connectionType, err := detectConnectionType(c.device) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | c.connectionType = connectionType 87 | 88 | switch c.connectionType { 89 | case ConnectionTypeBluetooth: 90 | _, err = c.device.GetFeatureReport(getFeatureReportCode0x04) 91 | if err != nil { 92 | return err 93 | } 94 | 95 | c.inputOffset = 2 96 | c.outputOffset = 3 97 | 98 | c.outputState = make([]byte, 79) 99 | c.outputState[0] = 0xA2 100 | c.outputState[1] = 0x11 101 | c.outputState[2] = 0x80 102 | c.outputState[4] = 0x0F 103 | case ConnectionTypeUSB: 104 | c.inputOffset = 0 105 | c.outputOffset = 0 106 | 107 | c.outputState = make([]byte, 79) 108 | c.outputState[0] = 0x05 109 | c.outputState[1] = 0x07 110 | } 111 | 112 | return nil 113 | } 114 | 115 | func (c *Controller) Disconnect() error { 116 | c.mutex.Lock() 117 | defer c.mutex.Unlock() 118 | 119 | err := c.errorIfNotConnected() 120 | if err != nil { 121 | return err 122 | } 123 | 124 | c.quit <- struct{}{} 125 | 126 | err = c.device.Close() 127 | if err != nil { 128 | return err 129 | } 130 | 131 | c.connectionType = ConnectionTypeNone 132 | 133 | c.errors <- nil 134 | 135 | return nil 136 | } 137 | 138 | func (c *Controller) ConnectionType() ConnectionType { 139 | c.mutex.RLock() 140 | defer c.mutex.RUnlock() 141 | 142 | return c.connectionType 143 | } 144 | 145 | func (c *Controller) Listen() error { 146 | c.mutex.Lock() 147 | 148 | err := c.errorIfNotConnected() 149 | if err != nil { 150 | return err 151 | } 152 | 153 | err = c.errorIfListening() 154 | if err != nil { 155 | return err 156 | } 157 | 158 | c.isListening = true 159 | defer func() { 160 | c.isListening = false 161 | }() 162 | 163 | go c.handle() 164 | 165 | c.mutex.Unlock() 166 | 167 | return <-c.errors 168 | } 169 | 170 | func (c *Controller) On(event Event, fn Callback) { 171 | c.emitter.setCallback(event, fn) 172 | } 173 | 174 | func (c *Controller) Off(event Event) { 175 | c.emitter.unsetCallback(event) 176 | } 177 | 178 | func (c *Controller) Rumble(rumble *rumble.Rumble) error { 179 | c.mutex.Lock() 180 | defer c.mutex.Unlock() 181 | 182 | err := c.errorIfNotConnected() 183 | if err != nil { 184 | return err 185 | } 186 | 187 | patch := make(map[uint]byte, 2) 188 | patch[4+c.outputOffset] = rumble.Left() 189 | patch[5+c.outputOffset] = rumble.Right() 190 | 191 | return c.set(patch) 192 | } 193 | 194 | func (c *Controller) Led(led *led.Led) error { 195 | c.mutex.Lock() 196 | defer c.mutex.Unlock() 197 | 198 | err := c.errorIfNotConnected() 199 | if err != nil { 200 | return err 201 | } 202 | 203 | patch := make(map[uint]byte, 3) 204 | patch[6+c.outputOffset] = led.Red() 205 | patch[7+c.outputOffset] = led.Green() 206 | patch[8+c.outputOffset] = led.Blue() 207 | patch[9+c.outputOffset] = led.FlashOn() 208 | patch[10+c.outputOffset] = led.FlashOff() 209 | 210 | return c.set(patch) 211 | } 212 | 213 | func (c *Controller) handle() { 214 | bytes := make([]byte, 64) 215 | bytes[0+c.inputOffset] = 1 216 | bytes[1+c.inputOffset] = 128 217 | bytes[2+c.inputOffset] = 128 218 | bytes[3+c.inputOffset] = 128 219 | bytes[4+c.inputOffset] = 128 220 | bytes[5+c.inputOffset] = 8 221 | 222 | c.inputPrevState = newState(bytes, c.inputOffset, nil) 223 | 224 | for { 225 | select { 226 | case <-c.quit: 227 | return 228 | default: 229 | _, err := c.device.Read(bytes) 230 | if err != nil { 231 | c.errors <- err 232 | 233 | return 234 | } 235 | 236 | c.inputCurrState = newState(bytes, c.inputOffset, c.inputPrevState) 237 | 238 | err = c.emitter.emit(c.inputCurrState, c.inputPrevState) 239 | if err != nil { 240 | c.errors <- err 241 | 242 | return 243 | } 244 | 245 | c.inputPrevState = c.inputCurrState 246 | } 247 | } 248 | } 249 | 250 | func (c *Controller) VendorID() uint16 { 251 | c.mutex.RLock() 252 | defer c.mutex.RUnlock() 253 | 254 | return c.device.VendorID() 255 | } 256 | 257 | func (c *Controller) ProductID() uint16 { 258 | c.mutex.RLock() 259 | defer c.mutex.RUnlock() 260 | 261 | return c.device.ProductID() 262 | } 263 | 264 | func (c *Controller) Name() string { 265 | c.mutex.RLock() 266 | defer c.mutex.RUnlock() 267 | 268 | return c.device.Product() 269 | } 270 | 271 | func (c *Controller) String() string { 272 | c.mutex.RLock() 273 | defer c.mutex.RUnlock() 274 | 275 | return fmt.Sprintf("%s (vendor: %v, product: %v)", c.device.Product(), c.device.VendorID(), c.device.ProductID()) 276 | } 277 | 278 | func (c *Controller) set(patch map[uint]byte) error { 279 | for i, b := range patch { 280 | c.outputState[i] = b 281 | } 282 | 283 | switch c.connectionType { 284 | case ConnectionTypeBluetooth: 285 | crc := crc32.ChecksumIEEE(c.outputState[0:75]) 286 | binary.LittleEndian.PutUint32(c.outputState[75:], crc) 287 | _, err := c.device.Write(c.outputState[1:]) 288 | if err != nil { 289 | return err 290 | } 291 | case ConnectionTypeUSB: 292 | _, err := c.device.Write(c.outputState) 293 | if err != nil { 294 | return err 295 | } 296 | } 297 | 298 | return nil 299 | } 300 | 301 | func (c *Controller) errorIfConnected() error { 302 | if c.connectionType != ConnectionTypeNone { 303 | return ErrControllerIsConnected 304 | } 305 | 306 | return nil 307 | } 308 | 309 | func (c *Controller) errorIfNotConnected() error { 310 | if c.connectionType == ConnectionTypeNone { 311 | return ErrControllerIsNotConnected 312 | } 313 | 314 | return nil 315 | } 316 | 317 | func (c *Controller) errorIfListening() error { 318 | if c.isListening { 319 | return ErrControllerIsListening 320 | } 321 | 322 | return nil 323 | } 324 | 325 | func Find() []*Controller { 326 | devices := hid.Find() 327 | controllers := make([]*Controller, 0, len(devices)) 328 | for _, device := range devices { 329 | controllers = append(controllers, NewController(device)) 330 | } 331 | 332 | return controllers 333 | } 334 | -------------------------------------------------------------------------------- /emitter.go: -------------------------------------------------------------------------------- 1 | package gods4 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type emitter struct { 8 | mutex sync.RWMutex 9 | callbacks map[Event]Callback 10 | checkers []func(currState, prevState *state) error 11 | } 12 | 13 | func (e *emitter) emit(currState, prevState *state) error { 14 | for _, checker := range e.checkers { 15 | err := checker(currState, prevState) 16 | if err != nil { 17 | return err 18 | } 19 | } 20 | 21 | return nil 22 | } 23 | 24 | func (e *emitter) callback(event Event) (Callback, bool) { 25 | e.mutex.RLock() 26 | defer e.mutex.RUnlock() 27 | 28 | if callback, ok := e.callbacks[event]; ok { 29 | return callback, true 30 | } 31 | 32 | return nil, false 33 | } 34 | 35 | func (e *emitter) setCallback(event Event, fn Callback) { 36 | e.mutex.Lock() 37 | defer e.mutex.Unlock() 38 | 39 | e.callbacks[event] = fn 40 | } 41 | 42 | func (e *emitter) unsetCallback(event Event) { 43 | e.mutex.Lock() 44 | defer e.mutex.Unlock() 45 | 46 | delete(e.callbacks, event) 47 | } 48 | 49 | func (e *emitter) checkBattery(currState, prevState *state) error { 50 | if currState.battery.Capacity != prevState.battery.Capacity || 51 | currState.battery.IsCharging != prevState.battery.IsCharging || 52 | currState.battery.IsCableConnected != prevState.battery.IsCableConnected { 53 | event := EventBatteryUpdate 54 | if callback, ok := e.callback(event); ok { 55 | err := callback(currState.battery) 56 | if err != nil { 57 | return err 58 | } 59 | } 60 | } 61 | 62 | return nil 63 | } 64 | 65 | func (e *emitter) checkTouchpad(currState, prevState *state) error { 66 | isSwipeChanged := false 67 | if len(currState.touchpad.Swipe) == len(prevState.touchpad.Swipe) { 68 | for i, currTouch := range currState.touchpad.Swipe { 69 | prevTouch := prevState.touchpad.Swipe[i] 70 | if currTouch.IsActive != prevTouch.IsActive || 71 | currTouch.X != prevTouch.X || 72 | currTouch.Y != prevTouch.Y { 73 | isSwipeChanged = true 74 | break 75 | } 76 | } 77 | } else { 78 | isSwipeChanged = true 79 | } 80 | 81 | if isSwipeChanged { 82 | event := EventTouchpadSwipe 83 | if callback, ok := e.callback(event); ok { 84 | err := callback(currState.touchpad) 85 | if err != nil { 86 | return err 87 | } 88 | } 89 | } 90 | 91 | if currState.touchpad.Press && !prevState.touchpad.Press { 92 | event := EventTouchpadPress 93 | if callback, ok := e.callback(event); ok { 94 | err := callback(currState.touchpad) 95 | if err != nil { 96 | return err 97 | } 98 | } 99 | } 100 | 101 | if !currState.touchpad.Press && prevState.touchpad.Press { 102 | event := EventTouchpadRelease 103 | if callback, ok := e.callback(event); ok { 104 | err := callback(currState.touchpad) 105 | if err != nil { 106 | return err 107 | } 108 | } 109 | } 110 | 111 | return nil 112 | } 113 | 114 | func (e *emitter) checkAccelerometer(currState, prevState *state) error { 115 | if currState.accelerometer.X != prevState.accelerometer.X || 116 | currState.accelerometer.Y != prevState.accelerometer.Y || 117 | currState.accelerometer.Z != prevState.accelerometer.Z { 118 | event := EventAccelerometerUpdate 119 | if callback, ok := e.callback(event); ok { 120 | err := callback(currState.accelerometer) 121 | if err != nil { 122 | return err 123 | } 124 | } 125 | } 126 | 127 | return nil 128 | } 129 | 130 | func (e *emitter) checkGyroscope(currState, prevState *state) error { 131 | if currState.gyroscope.Roll != prevState.gyroscope.Roll || 132 | currState.gyroscope.Yaw != prevState.gyroscope.Yaw || 133 | currState.gyroscope.Pitch != prevState.gyroscope.Pitch { 134 | event := EventGyroscopeUpdate 135 | if callback, ok := e.callback(event); ok { 136 | err := callback(currState.gyroscope) 137 | if err != nil { 138 | return err 139 | } 140 | } 141 | } 142 | 143 | return nil 144 | } 145 | 146 | func (e *emitter) checkRightStick(currState, prevState *state) error { 147 | if currState.rightStick.X != prevState.rightStick.X || 148 | currState.rightStick.Y != prevState.rightStick.Y { 149 | event := EventRightStickMove 150 | if callback, ok := e.callback(event); ok { 151 | err := callback(currState.rightStick) 152 | if err != nil { 153 | return err 154 | } 155 | } 156 | } 157 | 158 | return nil 159 | } 160 | 161 | func (e *emitter) checkLeftStick(currState, prevState *state) error { 162 | if currState.leftStick.X != prevState.leftStick.X || 163 | currState.leftStick.Y != prevState.leftStick.Y { 164 | event := EventLeftStickMove 165 | if callback, ok := e.callback(event); ok { 166 | err := callback(currState.leftStick) 167 | if err != nil { 168 | return err 169 | } 170 | } 171 | } 172 | 173 | return nil 174 | } 175 | 176 | func (e *emitter) checkPS(currState, prevState *state) error { 177 | if currState.ps && !prevState.ps { 178 | event := EventPSPress 179 | if callback, ok := e.callback(event); ok { 180 | err := callback(nil) 181 | if err != nil { 182 | return err 183 | } 184 | } 185 | } 186 | 187 | if !currState.ps && prevState.ps { 188 | event := EventPSRelease 189 | if callback, ok := e.callback(event); ok { 190 | err := callback(nil) 191 | if err != nil { 192 | return err 193 | } 194 | } 195 | } 196 | 197 | return nil 198 | } 199 | 200 | func (e *emitter) checkOptions(currState, prevState *state) error { 201 | if currState.options && !prevState.options { 202 | event := EventOptionsPress 203 | if callback, ok := e.callback(event); ok { 204 | err := callback(nil) 205 | if err != nil { 206 | return err 207 | } 208 | } 209 | } 210 | 211 | if !currState.options && prevState.options { 212 | event := EventOptionsRelease 213 | if callback, ok := e.callback(event); ok { 214 | err := callback(nil) 215 | if err != nil { 216 | return err 217 | } 218 | } 219 | } 220 | 221 | return nil 222 | } 223 | 224 | func (e *emitter) checkShare(currState, prevState *state) error { 225 | if currState.share && !prevState.share { 226 | event := EventSharePress 227 | if callback, ok := e.callback(event); ok { 228 | err := callback(nil) 229 | if err != nil { 230 | return err 231 | } 232 | } 233 | } 234 | 235 | if !currState.share && prevState.share { 236 | event := EventShareRelease 237 | if callback, ok := e.callback(event); ok { 238 | err := callback(nil) 239 | if err != nil { 240 | return err 241 | } 242 | } 243 | } 244 | 245 | return nil 246 | } 247 | 248 | func (e *emitter) checkDPad(currState, prevState *state) error { 249 | // D-pad up 250 | if currState.dPadUp && !prevState.dPadUp { 251 | event := EventDPadUpPress 252 | if callback, ok := e.callback(event); ok { 253 | err := callback(nil) 254 | if err != nil { 255 | return err 256 | } 257 | } 258 | } 259 | 260 | if !currState.dPadUp && prevState.dPadUp { 261 | event := EventDPadUpRelease 262 | if callback, ok := e.callback(event); ok { 263 | err := callback(nil) 264 | if err != nil { 265 | return err 266 | } 267 | } 268 | } 269 | 270 | // D-pad down 271 | if currState.dPadDown && !prevState.dPadDown { 272 | event := EventDPadDownPress 273 | if callback, ok := e.callback(event); ok { 274 | err := callback(nil) 275 | if err != nil { 276 | return err 277 | } 278 | } 279 | } 280 | 281 | if !currState.dPadDown && prevState.dPadDown { 282 | event := EventDPadDownRelease 283 | if callback, ok := e.callback(event); ok { 284 | err := callback(nil) 285 | if err != nil { 286 | return err 287 | } 288 | } 289 | } 290 | 291 | // D-pad left 292 | if currState.dPadLeft && !prevState.dPadLeft { 293 | event := EventDPadLeftPress 294 | if callback, ok := e.callback(event); ok { 295 | err := callback(nil) 296 | if err != nil { 297 | return err 298 | } 299 | } 300 | } 301 | 302 | if !currState.dPadLeft && prevState.dPadLeft { 303 | event := EventDPadLeftRelease 304 | if callback, ok := e.callback(event); ok { 305 | err := callback(nil) 306 | if err != nil { 307 | return err 308 | } 309 | } 310 | } 311 | 312 | // D-pad right 313 | if currState.dPadRight && !prevState.dPadRight { 314 | event := EventDPadRightPress 315 | if callback, ok := e.callback(event); ok { 316 | err := callback(nil) 317 | if err != nil { 318 | return err 319 | } 320 | } 321 | } 322 | 323 | if !currState.dPadRight && prevState.dPadRight { 324 | event := EventDPadRightRelease 325 | if callback, ok := e.callback(event); ok { 326 | err := callback(nil) 327 | if err != nil { 328 | return err 329 | } 330 | } 331 | } 332 | 333 | return nil 334 | } 335 | 336 | func (e *emitter) checkR3(currState, prevState *state) error { 337 | if currState.r3 && !prevState.r3 { 338 | event := EventR3Press 339 | if callback, ok := e.callback(event); ok { 340 | err := callback(nil) 341 | if err != nil { 342 | return err 343 | } 344 | } 345 | } 346 | 347 | if !currState.r3 && prevState.r3 { 348 | event := EventR3Release 349 | if callback, ok := e.callback(event); ok { 350 | err := callback(nil) 351 | if err != nil { 352 | return err 353 | } 354 | } 355 | } 356 | 357 | return nil 358 | } 359 | 360 | func (e *emitter) checkR2(currState, prevState *state) error { 361 | if currState.r2 != prevState.r2 { 362 | event := EventR2Press 363 | if callback, ok := e.callback(event); ok { 364 | err := callback(currState.r2) 365 | if err != nil { 366 | return err 367 | } 368 | } 369 | } 370 | 371 | if currState.r2 == 0 && prevState.r2 != 0 { 372 | event := EventR2Release 373 | if callback, ok := e.callback(event); ok { 374 | err := callback(currState.r2) 375 | if err != nil { 376 | return err 377 | } 378 | } 379 | } 380 | 381 | return nil 382 | } 383 | 384 | func (e *emitter) checkR1(currState, prevState *state) error { 385 | if currState.r1 && !prevState.r1 { 386 | event := EventR1Press 387 | if callback, ok := e.callback(event); ok { 388 | err := callback(nil) 389 | if err != nil { 390 | return err 391 | } 392 | } 393 | } 394 | 395 | if !currState.r1 && prevState.r1 { 396 | event := EventR1Release 397 | if callback, ok := e.callback(event); ok { 398 | err := callback(nil) 399 | if err != nil { 400 | return err 401 | } 402 | } 403 | } 404 | 405 | return nil 406 | } 407 | 408 | func (e *emitter) checkL3(currState, prevState *state) error { 409 | if currState.l3 && !prevState.l3 { 410 | event := EventL3Press 411 | if callback, ok := e.callback(event); ok { 412 | err := callback(nil) 413 | if err != nil { 414 | return err 415 | } 416 | } 417 | } 418 | 419 | if !currState.l3 && prevState.l3 { 420 | event := EventL3Release 421 | if callback, ok := e.callback(event); ok { 422 | err := callback(nil) 423 | if err != nil { 424 | return err 425 | } 426 | } 427 | } 428 | 429 | return nil 430 | } 431 | 432 | func (e *emitter) checkL2(currState, prevState *state) error { 433 | if currState.l2 != prevState.l2 { 434 | event := EventL2Press 435 | if callback, ok := e.callback(event); ok { 436 | err := callback(currState.l2) 437 | if err != nil { 438 | return err 439 | } 440 | } 441 | } 442 | 443 | if currState.l2 == 0 && prevState.l2 != 0 { 444 | event := EventL2Release 445 | if callback, ok := e.callback(event); ok { 446 | err := callback(currState.l2) 447 | if err != nil { 448 | return err 449 | } 450 | } 451 | } 452 | 453 | return nil 454 | } 455 | 456 | func (e *emitter) checkL1(currState, prevState *state) error { 457 | if currState.l1 && !prevState.l1 { 458 | event := EventL1Press 459 | if callback, ok := e.callback(event); ok { 460 | err := callback(nil) 461 | if err != nil { 462 | return err 463 | } 464 | } 465 | } 466 | 467 | if !currState.l1 && prevState.l1 { 468 | event := EventL1Release 469 | if callback, ok := e.callback(event); ok { 470 | err := callback(nil) 471 | if err != nil { 472 | return err 473 | } 474 | } 475 | } 476 | 477 | return nil 478 | } 479 | 480 | func (e *emitter) checkTriangle(currState, prevState *state) error { 481 | if currState.triangle && !prevState.triangle { 482 | event := EventTrianglePress 483 | if callback, ok := e.callback(event); ok { 484 | err := callback(nil) 485 | if err != nil { 486 | return err 487 | } 488 | } 489 | } 490 | 491 | if !currState.triangle && prevState.triangle { 492 | event := EventTriangleRelease 493 | if callback, ok := e.callback(event); ok { 494 | err := callback(nil) 495 | if err != nil { 496 | return err 497 | } 498 | } 499 | } 500 | 501 | return nil 502 | } 503 | 504 | func (e *emitter) checkSquare(currState, prevState *state) error { 505 | if currState.square && !prevState.square { 506 | event := EventSquarePress 507 | if callback, ok := e.callback(event); ok { 508 | err := callback(nil) 509 | if err != nil { 510 | return err 511 | } 512 | } 513 | } 514 | 515 | if !currState.square && prevState.square { 516 | event := EventSquareRelease 517 | if callback, ok := e.callback(event); ok { 518 | err := callback(nil) 519 | if err != nil { 520 | return err 521 | } 522 | } 523 | } 524 | 525 | return nil 526 | } 527 | 528 | func (e *emitter) checkCircle(currState, prevState *state) error { 529 | if currState.circle && !prevState.circle { 530 | event := EventCirclePress 531 | if callback, ok := e.callback(event); ok { 532 | err := callback(nil) 533 | if err != nil { 534 | return err 535 | } 536 | } 537 | } 538 | 539 | if !currState.circle && prevState.circle { 540 | event := EventCircleRelease 541 | if callback, ok := e.callback(event); ok { 542 | err := callback(nil) 543 | if err != nil { 544 | return err 545 | } 546 | } 547 | } 548 | 549 | return nil 550 | } 551 | 552 | func (e *emitter) checkCross(currState, prevState *state) error { 553 | if currState.cross && !prevState.cross { 554 | event := EventCrossPress 555 | if callback, ok := e.callback(event); ok { 556 | err := callback(nil) 557 | if err != nil { 558 | return err 559 | } 560 | } 561 | } 562 | 563 | if !currState.cross && prevState.cross { 564 | event := EventCrossRelease 565 | if callback, ok := e.callback(event); ok { 566 | err := callback(nil) 567 | if err != nil { 568 | return err 569 | } 570 | } 571 | } 572 | 573 | return nil 574 | } 575 | 576 | func newEmitter() *emitter { 577 | e := &emitter{callbacks: make(map[Event]Callback)} 578 | e.checkers = []func(currState, prevState *state) error{ 579 | e.checkCross, 580 | e.checkCircle, 581 | e.checkSquare, 582 | e.checkTriangle, 583 | e.checkL1, 584 | e.checkL2, 585 | e.checkL3, 586 | e.checkR1, 587 | e.checkR2, 588 | e.checkR3, 589 | e.checkDPad, 590 | e.checkShare, 591 | e.checkOptions, 592 | e.checkPS, 593 | e.checkLeftStick, 594 | e.checkRightStick, 595 | e.checkTouchpad, 596 | e.checkAccelerometer, 597 | e.checkGyroscope, 598 | e.checkBattery, 599 | } 600 | 601 | return e 602 | } 603 | -------------------------------------------------------------------------------- /event.go: -------------------------------------------------------------------------------- 1 | package gods4 2 | 3 | type Event string 4 | 5 | const ( 6 | // Cross 7 | EventCrossPress Event = "cross.press" 8 | EventCrossRelease Event = "cross.release" 9 | 10 | // Circle 11 | EventCirclePress Event = "circle.press" 12 | EventCircleRelease Event = "circle.release" 13 | 14 | // Square 15 | EventSquarePress Event = "square.press" 16 | EventSquareRelease Event = "square.release" 17 | 18 | // Triangle 19 | EventTrianglePress Event = "triangle.press" 20 | EventTriangleRelease Event = "triangle.release" 21 | 22 | // L1 23 | EventL1Press Event = "l1.press" 24 | EventL1Release Event = "l1.release" 25 | 26 | // L2 27 | EventL2Press Event = "l2.press" 28 | EventL2Release Event = "l2.release" 29 | 30 | // L3 31 | EventL3Press Event = "l3.press" 32 | EventL3Release Event = "l3.release" 33 | 34 | // R1 35 | EventR1Press Event = "r1.press" 36 | EventR1Release Event = "r1.release" 37 | 38 | // R2 39 | EventR2Press Event = "r2.press" 40 | EventR2Release Event = "r2.release" 41 | 42 | // R3 43 | EventR3Press Event = "r3.press" 44 | EventR3Release Event = "r3.release" 45 | 46 | // D-pad up 47 | EventDPadUpPress Event = "dpad_up.press" 48 | EventDPadUpRelease Event = "dpad_up.release" 49 | 50 | // D-pad down 51 | EventDPadDownPress Event = "dpad_down.press" 52 | EventDPadDownRelease Event = "dpad_down.release" 53 | 54 | // D-pad left 55 | EventDPadLeftPress Event = "dpad_left.press" 56 | EventDPadLeftRelease Event = "dpad_left.release" 57 | 58 | // D-pad right 59 | EventDPadRightPress Event = "dpad_right.press" 60 | EventDPadRightRelease Event = "dpad_right.release" 61 | 62 | // Share 63 | EventSharePress Event = "share.press" 64 | EventShareRelease Event = "share.release" 65 | 66 | // Options 67 | EventOptionsPress Event = "options.press" 68 | EventOptionsRelease Event = "options.release" 69 | 70 | // Touchpad 71 | EventTouchpadSwipe Event = "touchpad.swipe" 72 | EventTouchpadPress Event = "touchpad.press" 73 | EventTouchpadRelease Event = "touchpad.release" 74 | 75 | // PS 76 | EventPSPress Event = "ps.press" 77 | EventPSRelease Event = "ps.release" 78 | 79 | // Left stick 80 | EventLeftStickMove Event = "left_stick.move" 81 | 82 | // Right stick 83 | EventRightStickMove Event = "right_stick.move" 84 | 85 | // Accelerometer 86 | EventAccelerometerUpdate Event = "accelerometer.update" 87 | 88 | // Gyroscope 89 | EventGyroscopeUpdate Event = "gyroscope.update" 90 | 91 | // Battery 92 | EventBatteryUpdate Event = "battery.update" 93 | ) 94 | -------------------------------------------------------------------------------- /examples/demo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | 9 | "github.com/kpeu3i/gods4" 10 | "github.com/kpeu3i/gods4/led" 11 | "github.com/kpeu3i/gods4/rumble" 12 | ) 13 | 14 | func main() { 15 | // Find all controllers connected to your machine via USB or Bluetooth 16 | controllers := gods4.Find() 17 | if len(controllers) == 0 { 18 | panic("No connected DS4 controllers found") 19 | } 20 | 21 | // Select first controller from the list 22 | controller := controllers[0] 23 | 24 | // Connect to the controller 25 | err := controller.Connect() 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | log.Printf("* Controller #1 | %-10s | name: %s, connection: %s\n", "Connect", controller, controller.ConnectionType()) 31 | 32 | // Disconnect controller when a program is terminated 33 | signals := make(chan os.Signal, 1) 34 | signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) 35 | go func() { 36 | <-signals 37 | err := controller.Disconnect() 38 | if err != nil { 39 | panic(err) 40 | } 41 | log.Printf("* Controller #1 | %-10s | bye!\n", "Disconnect") 42 | }() 43 | 44 | // Register callback for "BatteryUpdate" event 45 | controller.On(gods4.EventBatteryUpdate, func(data interface{}) error { 46 | battery := data.(gods4.Battery) 47 | log.Printf("* Controller #1 | %-10s | capacity: %v%%, charging: %v, cable: %v\n", 48 | "Battery", 49 | battery.Capacity, 50 | battery.IsCharging, 51 | battery.IsCableConnected, 52 | ) 53 | 54 | return nil 55 | }) 56 | 57 | // Register callback for "CrossPress" event 58 | controller.On(gods4.EventCrossPress, func(data interface{}) error { 59 | log.Printf("* Controller #1 | %-10s | state: press\n", "Cross") 60 | 61 | return nil 62 | }) 63 | 64 | // Register callback for "CrossRelease" event 65 | controller.On(gods4.EventCrossRelease, func(data interface{}) error { 66 | log.Printf("* Controller #1 | %-10s | state: release\n", "Cross") 67 | 68 | return nil 69 | }) 70 | 71 | // Register callback for "RightStickMove" event 72 | controller.On(gods4.EventRightStickMove, func(data interface{}) error { 73 | stick := data.(gods4.Stick) 74 | log.Printf("* Controller #1 | %-10s | x: %v, y: %v\n", "RightStick", stick.X, stick.Y) 75 | 76 | return nil 77 | }) 78 | 79 | // Enable left and right rumble motors 80 | err = controller.Rumble(rumble.Both()) 81 | if err != nil { 82 | panic(err) 83 | } 84 | 85 | // Enable LED (yellow) with flash 86 | err = controller.Led(led.Yellow().Flash(50, 50)) 87 | if err != nil { 88 | panic(err) 89 | } 90 | 91 | // Start listening for controller events 92 | err = controller.Listen() 93 | if err != nil { 94 | panic(err) 95 | } 96 | 97 | // Output: 98 | // 2019/02/16 17:00:23 * Controller #1 | Connect | name: Wireless Controller (vendor: 1356, product: 2508), connection: BT 99 | // 2019/02/16 17:00:23 * Controller #1 | Battery | capacity: 77%, charging: false, cable: false 100 | // 2019/02/16 17:00:34 * Controller #1 | Cross | state: press 101 | // 2019/02/16 17:00:34 * Controller #1 | Cross | state: release 102 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 187, y: 98 103 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 191, y: 94 104 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 196, y: 93 105 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 212, y: 88 106 | // 2019/02/16 17:00:39 * Controller #1 | RightStick | x: 228, y: 79 107 | // 2019/02/16 17:02:52 * Controller #1 | Disconnect | bye! 108 | } 109 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kpeu3i/gods4 2 | 3 | require ( 4 | github.com/pkg/errors v0.8.0 5 | github.com/stamp/hid v0.0.0-20190105143849-bc55d7d13ce1 6 | ) 7 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 2 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 3 | github.com/stamp/hid v0.0.0-20190105143849-bc55d7d13ce1 h1:BG7MhC3qshAG6YSA1JiTFdQwXoOSmkAD9jHxfwIwcRM= 4 | github.com/stamp/hid v0.0.0-20190105143849-bc55d7d13ce1/go.mod h1:8a9qaZSBHmyhaqWfYpMnsvd4paEG6AGdl1TAy+gQkd4= 5 | -------------------------------------------------------------------------------- /hid/device.go: -------------------------------------------------------------------------------- 1 | package hid 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | "github.com/stamp/hid" 6 | ) 7 | 8 | var ( 9 | vendorIDs = [...]uint16{1356} 10 | productIDs = [...]uint16{2508, 1476} 11 | ) 12 | 13 | type Device struct { 14 | hidDeviceInfo *hid.DeviceInfo 15 | hidDevice *hid.Device 16 | } 17 | 18 | func (d *Device) VendorID() uint16 { 19 | return d.hidDeviceInfo.VendorID 20 | } 21 | 22 | func (d *Device) ProductID() uint16 { 23 | return d.hidDeviceInfo.ProductID 24 | } 25 | 26 | func (d *Device) Path() string { 27 | return d.hidDeviceInfo.Path 28 | } 29 | 30 | func (d *Device) Release() uint16 { 31 | return d.hidDeviceInfo.Release 32 | } 33 | 34 | func (d *Device) Serial() string { 35 | return d.hidDeviceInfo.Serial 36 | } 37 | 38 | func (d *Device) Manufacturer() string { 39 | return d.hidDeviceInfo.Manufacturer 40 | } 41 | 42 | func (d *Device) Product() string { 43 | return d.hidDeviceInfo.Product 44 | } 45 | 46 | func (d *Device) Open() error { 47 | hidDevice, err := d.hidDeviceInfo.Open() 48 | if err != nil { 49 | return err 50 | } 51 | 52 | d.hidDevice = hidDevice 53 | 54 | return nil 55 | } 56 | 57 | func (d *Device) Close() error { 58 | return d.hidDevice.Close() 59 | } 60 | 61 | func (d *Device) Read(b []byte) (int, error) { 62 | return d.hidDevice.Read(b) 63 | } 64 | 65 | func (d *Device) Write(b []byte) (int, error) { 66 | return d.hidDevice.Write(b) 67 | } 68 | 69 | func (d *Device) GetFeatureReport(code byte) ([]byte, error) { 70 | var bytes []byte 71 | 72 | switch code { 73 | case 0x04: 74 | bytes = make([]byte, 67) 75 | bytes[0] = code 76 | _, err := d.hidDevice.GetFeatureReport(bytes) 77 | if err != nil { 78 | return nil, err 79 | } 80 | default: 81 | return nil, errors.Errorf("hid: unsupported report code: %v", code) 82 | } 83 | 84 | return bytes, nil 85 | } 86 | 87 | func Find() []*Device { 88 | var devices []*Device 89 | 90 | for _, vendorID := range vendorIDs { 91 | for _, productID := range productIDs { 92 | for _, info := range hid.Enumerate(vendorID, productID) { 93 | devices = append(devices, &Device{hidDeviceInfo: &info}) 94 | } 95 | } 96 | } 97 | 98 | return devices 99 | } 100 | -------------------------------------------------------------------------------- /led/led.go: -------------------------------------------------------------------------------- 1 | package led 2 | 3 | type Led struct { 4 | red byte 5 | green byte 6 | blue byte 7 | flashOn byte 8 | flashOff byte 9 | } 10 | 11 | func (l *Led) Red() byte { 12 | return l.red 13 | } 14 | 15 | func (l *Led) Green() byte { 16 | return l.green 17 | } 18 | 19 | func (l *Led) Blue() byte { 20 | return l.blue 21 | } 22 | 23 | func (l *Led) FlashOn() byte { 24 | return l.flashOn 25 | } 26 | 27 | func (l *Led) FlashOff() byte { 28 | return l.flashOff 29 | } 30 | 31 | func (l *Led) Flash(on, off byte) *Led { 32 | l.flashOn = on 33 | l.flashOff = off 34 | 35 | return l 36 | } 37 | 38 | func RGB(red, green, blue byte) *Led { 39 | return &Led{red: red, green: green, blue: blue} 40 | } 41 | 42 | func None() *Led { 43 | return &Led{} 44 | } 45 | 46 | func White() *Led { 47 | return &Led{red: 255, green: 255, blue: 255} 48 | } 49 | 50 | func Red() *Led { 51 | return &Led{red: 255} 52 | } 53 | 54 | func Green() *Led { 55 | return &Led{green: 128} 56 | } 57 | 58 | func Blue() *Led { 59 | return &Led{blue: 255} 60 | } 61 | 62 | func Lime() *Led { 63 | return &Led{green: 255} 64 | } 65 | 66 | func Yellow() *Led { 67 | return &Led{red: 255, green: 255} 68 | } 69 | 70 | func Cyan() *Led { 71 | return &Led{green: 255, blue: 255} 72 | } 73 | 74 | func Magenta() *Led { 75 | return &Led{red: 255, blue: 255} 76 | } 77 | 78 | func Silver() *Led { 79 | return &Led{red: 192, green: 192, blue: 192} 80 | } 81 | 82 | func Gray() *Led { 83 | return &Led{red: 128, green: 128, blue: 128} 84 | } 85 | 86 | func Maroon() *Led { 87 | return &Led{red: 128} 88 | } 89 | 90 | func Olive() *Led { 91 | return &Led{red: 128, green: 128} 92 | } 93 | 94 | func Purple() *Led { 95 | return &Led{red: 128, blue: 128} 96 | } 97 | 98 | func Teal() *Led { 99 | return &Led{green: 128, blue: 128} 100 | } 101 | 102 | func Navy() *Led { 103 | return &Led{blue: 128} 104 | } 105 | -------------------------------------------------------------------------------- /rumble/rumble.go: -------------------------------------------------------------------------------- 1 | package rumble 2 | 3 | type Rumble struct { 4 | left byte 5 | right byte 6 | } 7 | 8 | func (r *Rumble) Left() byte { 9 | return r.left 10 | } 11 | 12 | func (r *Rumble) Right() byte { 13 | return r.right 14 | } 15 | 16 | func New(left, right byte) *Rumble { 17 | return &Rumble{left: left, right: right} 18 | } 19 | 20 | func Left() *Rumble { 21 | return &Rumble{left: 255} 22 | } 23 | 24 | func Right() *Rumble { 25 | return &Rumble{right: 255} 26 | } 27 | 28 | func Both() *Rumble { 29 | return &Rumble{left: 255, right: 255} 30 | } 31 | -------------------------------------------------------------------------------- /state.go: -------------------------------------------------------------------------------- 1 | package gods4 2 | 3 | import ( 4 | "encoding/binary" 5 | "math" 6 | ) 7 | 8 | const analogSticksSmoothing = 4 9 | 10 | type state struct { 11 | cross bool 12 | circle bool 13 | square bool 14 | triangle bool 15 | l1 bool 16 | l2 byte 17 | l3 bool 18 | r1 bool 19 | r2 byte 20 | r3 bool 21 | dPadUp bool 22 | dPadDown bool 23 | dPadLeft bool 24 | dPadRight bool 25 | share bool 26 | options bool 27 | ps bool 28 | leftStick Stick 29 | rightStick Stick 30 | touchpad Touchpad 31 | accelerometer Accelerometer 32 | gyroscope Gyroscope 33 | battery Battery 34 | } 35 | 36 | type Stick struct { 37 | X byte 38 | Y byte 39 | } 40 | 41 | type Touchpad struct { 42 | Press bool 43 | Swipe []Touch 44 | } 45 | 46 | type Touch struct { 47 | IsActive bool 48 | X byte 49 | Y byte 50 | } 51 | 52 | type Accelerometer struct { 53 | X int16 54 | Y int16 55 | Z int16 56 | } 57 | 58 | type Gyroscope struct { 59 | Roll int16 60 | Yaw int16 61 | Pitch int16 62 | } 63 | 64 | type Battery struct { 65 | Capacity byte 66 | IsCharging bool 67 | IsCableConnected bool 68 | } 69 | 70 | func newState(bytes []byte, offset uint, prevState *state) *state { 71 | s := &state{ 72 | cross: buttonCrossState(bytes, offset), 73 | circle: buttonCircleState(bytes, offset), 74 | square: buttonSquareState(bytes, offset), 75 | triangle: buttonTriangleState(bytes, offset), 76 | l1: buttonL1State(bytes, offset), 77 | l2: buttonL2State(bytes, offset), 78 | l3: buttonL3State(bytes, offset), 79 | r1: buttonR1State(bytes, offset), 80 | r2: buttonR2State(bytes, offset), 81 | r3: buttonR3State(bytes, offset), 82 | dPadUp: buttonDPadUpState(bytes, offset), 83 | dPadDown: buttonDPadDownState(bytes, offset), 84 | dPadLeft: buttonDPadLeftState(bytes, offset), 85 | dPadRight: buttonDPadRightState(bytes, offset), 86 | share: buttonShareState(bytes, offset), 87 | options: buttonOptionsState(bytes, offset), 88 | ps: buttonPSState(bytes, offset), 89 | leftStick: buttonLeftStickState(bytes, offset, prevState), 90 | rightStick: buttonRightStickState(bytes, offset, prevState), 91 | touchpad: touchpadState(bytes, offset), 92 | accelerometer: accelerometerState(bytes, offset), 93 | gyroscope: gyroscopeState(bytes, offset), 94 | battery: batteryState(bytes, offset), 95 | } 96 | 97 | return s 98 | } 99 | 100 | func buttonCrossState(bytes []byte, offset uint) bool { 101 | return bytes[5+offset]&32 != 0 102 | } 103 | 104 | func buttonCircleState(bytes []byte, offset uint) bool { 105 | return bytes[5+offset]&64 != 0 106 | } 107 | 108 | func buttonSquareState(bytes []byte, offset uint) bool { 109 | return bytes[5+offset]&16 != 0 110 | } 111 | 112 | func buttonTriangleState(bytes []byte, offset uint) bool { 113 | return bytes[5+offset]&128 != 0 114 | } 115 | 116 | func buttonL1State(bytes []byte, offset uint) bool { 117 | return bytes[6+offset]&1 != 0 118 | } 119 | 120 | func buttonL2State(bytes []byte, offset uint) byte { 121 | if bytes[6+offset]&4 != 0 { 122 | return bytes[8+offset] 123 | } 124 | 125 | return 0 126 | } 127 | 128 | func buttonL3State(bytes []byte, offset uint) bool { 129 | return bytes[6+offset]&64 != 0 130 | } 131 | 132 | func buttonR1State(bytes []byte, offset uint) bool { 133 | return bytes[6+offset]&2 != 0 134 | } 135 | 136 | func buttonR2State(bytes []byte, offset uint) byte { 137 | if bytes[6+offset]&8 != 0 { 138 | return bytes[9+offset] 139 | } 140 | 141 | return 0 142 | } 143 | 144 | func buttonR3State(bytes []byte, offset uint) bool { 145 | return bytes[6+offset]&128 != 0 146 | } 147 | 148 | func buttonDPadUpState(bytes []byte, offset uint) bool { 149 | v := bytes[5+offset] & 15 150 | 151 | return v == 0 || v == 1 || v == 7 152 | } 153 | 154 | func buttonDPadDownState(bytes []byte, offset uint) bool { 155 | v := bytes[5+offset] & 15 156 | 157 | return v == 3 || v == 4 || v == 5 158 | } 159 | 160 | func buttonDPadLeftState(bytes []byte, offset uint) bool { 161 | v := bytes[5+offset] & 15 162 | 163 | return v == 5 || v == 6 || v == 7 164 | } 165 | 166 | func buttonDPadRightState(bytes []byte, offset uint) bool { 167 | v := bytes[5+offset] & 15 168 | 169 | return v == 1 || v == 2 || v == 3 170 | } 171 | 172 | func buttonShareState(bytes []byte, offset uint) bool { 173 | return bytes[6+offset]&16 != 0 174 | } 175 | 176 | func buttonOptionsState(bytes []byte, offset uint) bool { 177 | return bytes[6+offset]&32 != 0 178 | } 179 | 180 | //func buttonTouchpadState(bytes []byte, offset uint) bool { 181 | // return bytes[7+offset]&2 != 0 182 | //} 183 | 184 | func buttonPSState(bytes []byte, offset uint) bool { 185 | return bytes[7+offset]&1 != 0 186 | } 187 | 188 | func buttonLeftStickState(bytes []byte, offset uint, prevState *state) Stick { 189 | var prevX, prevY byte 190 | 191 | if prevState == nil { 192 | prevX, prevY = bytes[1+offset], bytes[2+offset] 193 | } else { 194 | prevX, prevY = prevState.leftStick.X, prevState.leftStick.Y 195 | } 196 | 197 | if math.Abs(float64(bytes[1+offset])-float64(prevX)) >= float64(analogSticksSmoothing) || 198 | math.Abs(float64(bytes[2+offset])-float64(prevY)) >= float64(analogSticksSmoothing) { 199 | return Stick{X: bytes[1+offset], Y: bytes[2+offset]} 200 | } 201 | 202 | return Stick{X: prevX, Y: prevY} 203 | } 204 | 205 | func buttonRightStickState(bytes []byte, offset uint, prevState *state) Stick { 206 | var prevX, prevY byte 207 | 208 | if prevState == nil { 209 | prevX, prevY = bytes[3+offset], bytes[4+offset] 210 | } else { 211 | prevX, prevY = prevState.rightStick.X, prevState.rightStick.Y 212 | } 213 | 214 | if math.Abs(float64(bytes[3+offset])-float64(prevX)) >= float64(analogSticksSmoothing) || 215 | math.Abs(float64(bytes[4+offset])-float64(prevY)) >= float64(analogSticksSmoothing) { 216 | return Stick{X: bytes[3+offset], Y: bytes[4+offset]} 217 | } 218 | 219 | return Stick{X: prevX, Y: prevY} 220 | } 221 | 222 | func touchpadState(bytes []byte, offset uint) Touchpad { 223 | var ( 224 | touches []Touch 225 | touchOffset uint 226 | ) 227 | 228 | for i := 1; i <= 2; i++ { 229 | touch := Touch{ 230 | IsActive: (bytes[35+touchOffset+offset] >> 7) == 0, 231 | X: ((bytes[37+touchOffset+offset] & 0x0F) << 8) | bytes[36+touchOffset+offset], 232 | Y: bytes[38+touchOffset+offset]<<4 | ((bytes[37+touchOffset+offset] & 0xF0) >> 4), 233 | } 234 | 235 | touches = append(touches, touch) 236 | touchOffset += 4 237 | } 238 | 239 | t := Touchpad{ 240 | Press: bytes[7+offset]&2 != 0, 241 | Swipe: touches, 242 | } 243 | 244 | return t 245 | } 246 | 247 | func accelerometerState(bytes []byte, offset uint) Accelerometer { 248 | a := Accelerometer{ 249 | X: int16(binary.LittleEndian.Uint16(bytes[13+offset:])), 250 | Y: -int16(binary.LittleEndian.Uint16(bytes[15+offset:])), 251 | Z: -int16(binary.LittleEndian.Uint16(bytes[17+offset:])), 252 | } 253 | 254 | return a 255 | } 256 | 257 | func gyroscopeState(bytes []byte, offset uint) Gyroscope { 258 | g := Gyroscope{ 259 | Roll: -int16(binary.LittleEndian.Uint16(bytes[19+offset:])), 260 | Yaw: int16(binary.LittleEndian.Uint16(bytes[21+offset:])), 261 | Pitch: int16(binary.LittleEndian.Uint16(bytes[23+offset:])), 262 | } 263 | 264 | return g 265 | } 266 | 267 | func batteryState(bytes []byte, offset uint) Battery { 268 | var ( 269 | isCharging bool 270 | maxCapacity byte 271 | ) 272 | 273 | capacity := bytes[30+offset] & 0x0F 274 | isCableConnected := ((bytes[30+offset] >> 4) & 0x01) == 1 275 | 276 | if !isCableConnected || capacity > 10 { 277 | isCharging = false 278 | } else { 279 | isCharging = true 280 | } 281 | 282 | if isCableConnected { 283 | maxCapacity = 10 284 | } else { 285 | maxCapacity = 9 286 | } 287 | 288 | if capacity > maxCapacity { 289 | capacity = maxCapacity 290 | } 291 | 292 | capacity = byte(float64(float64(capacity) / float64(maxCapacity) * 100)) 293 | 294 | battery := Battery{ 295 | Capacity: capacity, 296 | IsCharging: isCharging, 297 | IsCableConnected: isCableConnected, 298 | } 299 | 300 | return battery 301 | } 302 | --------------------------------------------------------------------------------